mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 15:20:13 +01:00
Merge branch 'master' of git://github.com/bsag/tracks
Conflicts: config/routes.rb
This commit is contained in:
commit
952a73e39d
1092 changed files with 52802 additions and 20798 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
*.tmproj
|
||||
config/database.yml
|
||||
config/environment.rb
|
||||
log
|
||||
|
|
@ -5,3 +6,4 @@ tmp
|
|||
db/data.yml
|
||||
db/*.sqlite3
|
||||
nbproject
|
||||
vendor/plugins/query_trace/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Likewise will all the methods added be available for all controllers.
|
||||
|
||||
require_dependency "login_system"
|
||||
require_dependency "source_view"
|
||||
require_dependency "tracks/source_view"
|
||||
require "redcloth"
|
||||
|
||||
require 'date'
|
||||
|
|
@ -20,6 +20,7 @@ class ApplicationController < ActionController::Base
|
|||
layout proc{ |controller| controller.mobile? ? "mobile" : "standard" }
|
||||
|
||||
before_filter :set_session_expiration
|
||||
before_filter :set_time_zone
|
||||
prepend_before_filter :login_required
|
||||
prepend_before_filter :enable_mobile_content_negotiation
|
||||
after_filter :restore_content_type_for_mobile
|
||||
|
|
@ -178,6 +179,14 @@ class ApplicationController < ActionController::Base
|
|||
raise ArgumentError.new("invalid value for Boolean: \"#{s}\"")
|
||||
end
|
||||
|
||||
def self.openid_enabled?
|
||||
Tracks::Config.openid_enabled?
|
||||
end
|
||||
|
||||
def openid_enabled?
|
||||
self.class.openid_enabled?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_date_per_user_prefs( s )
|
||||
|
|
@ -213,4 +222,8 @@ class ApplicationController < ActionController::Base
|
|||
logger.error("ERROR: #{message}") if type == :error
|
||||
end
|
||||
|
||||
def set_time_zone
|
||||
Time.zone = current_user.prefs.time_zone if logged_in?
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ class LoginController < ApplicationController
|
|||
skip_before_filter :login_required
|
||||
before_filter :login_optional
|
||||
before_filter :get_current_user
|
||||
open_id_consumer if Tracks::Config.openid_enabled?
|
||||
open_id_consumer if openid_enabled?
|
||||
|
||||
def login
|
||||
@page_title = "TRACKS::Login"
|
||||
@openid_url = cookies[:openid_url] if Tracks::Config.openid_enabled?
|
||||
@openid_url = cookies[:openid_url] if openid_enabled?
|
||||
case request.method
|
||||
when :post
|
||||
if @user = User.authenticate(params['user_login'], params['user_password'])
|
||||
|
|
|
|||
|
|
@ -415,7 +415,7 @@ class StatsController < ApplicationController
|
|||
@max=0
|
||||
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
||||
@actions_creation_hour.each do |r|
|
||||
hour = current_user.prefs.tz.adjust(r.created_at).hour
|
||||
hour = r.created_at.hour
|
||||
@actions_creation_hour_array[hour] += 1
|
||||
end
|
||||
0.upto(23) { |i| @max = @actions_creation_hour_array[i] if @actions_creation_hour_array[i] > @max}
|
||||
|
|
@ -423,7 +423,7 @@ class StatsController < ApplicationController
|
|||
# convert to hash to be able to fill in non-existing days
|
||||
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
||||
@actions_completion_hour.each do |r|
|
||||
hour = current_user.prefs.tz.adjust(r.completed_at).hour
|
||||
hour = r.completed_at.hour
|
||||
@actions_completion_hour_array[hour] += 1
|
||||
end
|
||||
0.upto(23) { |i| @max = @actions_completion_hour_array[i] if @actions_completion_hour_array[i] > @max}
|
||||
|
|
@ -446,7 +446,7 @@ class StatsController < ApplicationController
|
|||
@max=0
|
||||
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
||||
@actions_creation_hour.each do |r|
|
||||
hour = current_user.prefs.tz.adjust(r.created_at).hour
|
||||
hour = r.created_at.hour
|
||||
@actions_creation_hour_array[hour] += 1
|
||||
end
|
||||
0.upto(23) { |i| @max = @actions_creation_hour_array[i] if @actions_creation_hour_array[i] > @max}
|
||||
|
|
@ -454,7 +454,7 @@ class StatsController < ApplicationController
|
|||
# convert to hash to be able to fill in non-existing days
|
||||
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
||||
@actions_completion_hour.each do |r|
|
||||
hour = current_user.prefs.tz.adjust(r.completed_at).hour
|
||||
hour = r.completed_at.hour
|
||||
@actions_completion_hour_array[hour] += 1
|
||||
end
|
||||
0.upto(23) { |i| @max = @actions_completion_hour_array[i] if @actions_completion_hour_array[i] > @max}
|
||||
|
|
|
|||
|
|
@ -267,9 +267,9 @@ class TodosController < ApplicationController
|
|||
def completed
|
||||
@page_title = "TRACKS::Completed tasks"
|
||||
@done = current_user.completed_todos
|
||||
@done_today = @done.completed_within current_user.time - 1.day
|
||||
@done_this_week = @done.completed_within current_user.time - 1.week
|
||||
@done_this_month = @done.completed_within current_user.time - 4.week
|
||||
@done_today = @done.completed_within Time.zone.now - 1.day
|
||||
@done_this_week = @done.completed_within Time.zone.now - 1.week
|
||||
@done_this_month = @done.completed_within Time.zone.now - 4.week
|
||||
@count = @done_today.size + @done_this_week.size + @done_this_month.size
|
||||
end
|
||||
|
||||
|
|
@ -277,7 +277,7 @@ class TodosController < ApplicationController
|
|||
@page_title = "TRACKS::Archived completed tasks"
|
||||
@done = current_user.completed_todos
|
||||
@count = @done.size
|
||||
@done_archive = @done.completed_more_than current_user.time - 28.days
|
||||
@done_archive = @done.completed_more_than Time.zone.now - 28.days
|
||||
end
|
||||
|
||||
def list_deferred
|
||||
|
|
@ -392,7 +392,7 @@ class TodosController < ApplicationController
|
|||
|
||||
if params.key?('due')
|
||||
due_within = params['due'].to_i
|
||||
due_within_when = current_user.time + due_within.days
|
||||
due_within_when = Time.zone.now + due_within.days
|
||||
condition_builder.add('todos.due <= ?', due_within_when)
|
||||
due_within_date_s = due_within_when.strftime("%Y-%m-%d")
|
||||
@title << " due today" if (due_within == 0)
|
||||
|
|
@ -402,7 +402,7 @@ class TodosController < ApplicationController
|
|||
|
||||
if params.key?('done')
|
||||
done_in_last = params['done'].to_i
|
||||
condition_builder.add('todos.completed_at >= ?', current_user.time - done_in_last.days)
|
||||
condition_builder.add('todos.completed_at >= ?', Time.zone.now - done_in_last.days)
|
||||
@title << " actions completed"
|
||||
@description << " in the last #{done_in_last.to_s} days"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class UsersController < ApplicationController
|
||||
|
||||
if Tracks::Config.openid_enabled?
|
||||
if openid_enabled?
|
||||
open_id_consumer
|
||||
before_filter :begin_open_id_auth, :only => :update_auth_type
|
||||
end
|
||||
|
|
@ -153,7 +153,7 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def update_auth_type
|
||||
if (params[:user][:auth_type] == 'open_id') && Tracks::Config.openid_enabled?
|
||||
if (params[:user][:auth_type] == 'open_id') && openid_enabled?
|
||||
case open_id_response.status
|
||||
when OpenID::SUCCESS
|
||||
# The URL was a valid identity URL. Now we just need to send a redirect
|
||||
|
|
@ -179,7 +179,7 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def complete
|
||||
return unless Tracks::Config.openid_enabled?
|
||||
return unless openid_enabled?
|
||||
openid_url = session['openid_url']
|
||||
if openid_url.blank?
|
||||
notify :error, "expected an openid_url"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module ApplicationHelper
|
||||
|
||||
def user_time
|
||||
current_user.time
|
||||
Time.zone.now
|
||||
end
|
||||
|
||||
# Replicates the link_to method but also checks request.request_uri to find
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
class Preference < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
composed_of :tz,
|
||||
:class_name => 'TimeZone',
|
||||
:mapping => %w(time_zone name)
|
||||
|
||||
def self.due_styles
|
||||
{ :due_in_n_days => 0, :due_on => 1}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
class Project < ActiveRecord::Base
|
||||
has_many :todos, :dependent => :delete_all, :include => :context
|
||||
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
||||
belongs_to :default_context, :dependent => :nullify, :class_name => "Context", :foreign_key => "default_context_id"
|
||||
belongs_to :default_context, :class_name => "Context", :foreign_key => "default_context_id"
|
||||
belongs_to :user
|
||||
|
||||
validates_presence_of :name, :message => "project must have a name"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class Todo < ActiveRecord::Base
|
||||
|
||||
belongs_to :context, :order => 'name'
|
||||
belongs_to :context
|
||||
belongs_to :project
|
||||
belongs_to :user
|
||||
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def time
|
||||
prefs.tz.adjust(Time.now.utc)
|
||||
Time.now.in_time_zone(prefs.time_zone)
|
||||
end
|
||||
|
||||
def date
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<li>Last name: <span class="highlight"><%= current_user.last_name %></span></li>
|
||||
<li>Date format: <span class="highlight"><%= prefs.date_format %></span> Your current date: <%= format_date(user_time) %></li>
|
||||
<li>Title date format: <span class="highlight"><%= prefs.title_date_format %></span> Your current title date: <%= user_time.strftime(prefs.title_date_format) %></li>
|
||||
<li>Time zone: <span class="highlight"><%= prefs.tz %></span> Your current time: <%= user_time.strftime('%I:%M %p') %></li>
|
||||
<li>Time zone: <span class="highlight"><%= prefs.time_zone %></span> Your current time: <%= user_time.strftime('%I:%M %p') %></li>
|
||||
<li>Week starts on: <span class="highlight"><%= Preference.day_number_to_name_map[prefs.week_starts] %></span></li>
|
||||
<li>Show the last <span class="highlight"><%= prefs.show_number_completed %></span> completed items</li>
|
||||
<li>Show completed projects in sidebar: <span class="highlight"><%= prefs.show_completed_projects_in_sidebar %></span></li>
|
||||
|
|
|
|||
|
|
@ -24,9 +24,8 @@ module Rails
|
|||
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
||||
end
|
||||
|
||||
# FIXME : Ruby 1.9
|
||||
def preinitialize
|
||||
load(preinitializer_path) if File.exists?(preinitializer_path)
|
||||
load(preinitializer_path) if File.exist?(preinitializer_path)
|
||||
end
|
||||
|
||||
def preinitializer_path
|
||||
|
|
@ -44,6 +43,7 @@ module Rails
|
|||
class VendorBoot < Boot
|
||||
def load_initializer
|
||||
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
||||
Rails::Initializer.run(:install_gem_spec_stubs)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ Rails::Initializer.run do |config|
|
|||
# Make Active Record use UTC-base instead of local time
|
||||
config.active_record.default_timezone = :utc
|
||||
|
||||
# You''ll probably want to change this to the time zone of the computer where Tracks is running
|
||||
# run rake time:zones:local have Rails suggest time zone names on your system
|
||||
config.time_zone = 'UTC'
|
||||
|
||||
# Use Active Record's schema dumper instead of SQL when creating the test database
|
||||
# (enables use of different database adapters for development and test environments)
|
||||
config.active_record.schema_format = :ruby
|
||||
|
|
@ -66,18 +70,13 @@ end
|
|||
|
||||
# Include your application configuration below
|
||||
|
||||
# Time zone setting. Set your local time zone here. #
|
||||
# You should be able to find a list of time zones in /usr/share/zoneinfo
|
||||
# e.g. if you are in the Eastern time zone of the US, set the value below.
|
||||
# ENV['TZ'] = 'US/Eastern'
|
||||
|
||||
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
|
||||
# If you choose ldap, see the additional configuration options further down.
|
||||
AUTHENTICATION_SCHEMES = ['database']
|
||||
|
||||
require 'name_part_finder'
|
||||
require 'todo_list'
|
||||
require 'config'
|
||||
require 'tracks/todo_list'
|
||||
require 'tracks/config'
|
||||
require 'activerecord_base_tag_extensions' # Needed for tagging-specific extensions
|
||||
require 'digest/sha1' #Needed to support 'rake db:fixtures:load' on some ruby installs: http://dev.rousette.org.uk/ticket/557
|
||||
require 'prototype_helper_extensions'
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ config.action_controller.allow_forgery_protection = false
|
|||
# config.pre_loaded_fixtures = false
|
||||
SALT = "change-me" unless defined?( SALT ).nil?
|
||||
|
||||
config.time_zone = 'UTC'
|
||||
|
||||
config.after_initialize do
|
||||
require File.expand_path(File.dirname(__FILE__) + "/../../test/selenium_helper")
|
||||
end
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
<<<<<<< HEAD:config/routes.rb
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
UJS::routes
|
||||
|
||||
|
|
@ -62,3 +63,73 @@ ActionController::Routing::Routes.draw do |map|
|
|||
map.connect ':controller/:action/:id'
|
||||
|
||||
end
|
||||
=======
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
UJS::routes
|
||||
|
||||
map.with_options :controller => 'login' do |login|
|
||||
login.login 'login', :action => 'login'
|
||||
login.formatted_login 'login.:format', :action => 'login'
|
||||
login.logout 'logout', :action => 'logout'
|
||||
login.formatted_logout 'logout.:format', :action => 'logout'
|
||||
login.open_id_begin 'begin', :action => 'begin'
|
||||
login.formatted_open_id_begin 'begin.:format', :action => 'begin'
|
||||
login.open_id_complete 'complete', :action => 'complete'
|
||||
login.formatted_open_id_complete 'complete.:format', :action => 'complete'
|
||||
end
|
||||
|
||||
map.resources :users,
|
||||
:member => {:change_password => :get, :update_password => :post,
|
||||
:change_auth_type => :get, :update_auth_type => :post, :complete => :get,
|
||||
:refresh_token => :post }
|
||||
map.with_options :controller => "users" do |users|
|
||||
users.signup 'signup', :action => "new"
|
||||
end
|
||||
|
||||
map.resources :contexts, :collection => {:order => :post} do |contexts|
|
||||
contexts.resources :todos, :name_prefix => "context_"
|
||||
end
|
||||
|
||||
map.resources :projects, :collection => {:order => :post, :alphabetize => :post} do |projects|
|
||||
projects.resources :todos, :name_prefix => "project_"
|
||||
end
|
||||
|
||||
map.resources :todos,
|
||||
:member => {:toggle_check => :put, :toggle_star => :put},
|
||||
:collection => {:check_deferred => :post, :filter_to_context => :post, :filter_to_project => :post}
|
||||
map.with_options :controller => "todos" do |todos|
|
||||
todos.home '', :action => "index"
|
||||
todos.tickler 'tickler', :action => "list_deferred"
|
||||
todos.mobile_tickler 'tickler.m', :action => "list_deferred", :format => 'm'
|
||||
todos.done 'done', :action => "completed"
|
||||
todos.done_archive 'done/archive', :action => "completed_archive"
|
||||
|
||||
# This route works for tags with dots like /todos/tag/version1.5
|
||||
# please note that this pattern consumes everything after /todos/tag
|
||||
# so /todos/tag/version1.5.xml will result in :name => 'version1.5.xml'
|
||||
# UPDATE: added support for mobile view. All tags ending on .m will be
|
||||
# routed to mobile view of tags.
|
||||
todos.tag 'todos/tag/:name', :action => "tag", :format => 'm', :name => /.*\.m/
|
||||
todos.tag 'todos/tag/:name', :action => "tag", :name => /.*/
|
||||
|
||||
todos.mobile 'mobile', :action => "index", :format => 'm'
|
||||
todos.mobile_abbrev 'm', :action => "index", :format => 'm'
|
||||
todos.mobile_abbrev_new 'm/new', :action => "new", :format => 'm'
|
||||
end
|
||||
|
||||
map.resources :notes
|
||||
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
||||
map.feeds 'feeds.m', :controller => 'feedlist', :action => 'index', :format => 'm'
|
||||
|
||||
map.preferences 'preferences', :controller => 'preferences', :action => 'index'
|
||||
map.integrations 'integrations', :controller => 'integrations', :action => 'index'
|
||||
|
||||
if Rails.env == 'test'
|
||||
map.connect '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login'
|
||||
end
|
||||
|
||||
# Install the default route as the lowest priority.
|
||||
map.connect ':controller/:action/:id'
|
||||
|
||||
end
|
||||
>>>>>>> 0600756bbf8ab3f3654cbb0e6bcbf9ea03068918:config/routes.rb
|
||||
|
|
|
|||
74
db/schema.rb
74
db/schema.rb
|
|
@ -1,5 +1,5 @@
|
|||
# This file is auto-generated from the current state of the database. Instead of editing this file,
|
||||
# please use the migrations feature of ActiveRecord to incrementally modify your database, and
|
||||
# please use the migrations feature of Active Record to incrementally modify your database, and
|
||||
# then regenerate this schema definition.
|
||||
#
|
||||
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
|
||||
|
|
@ -9,43 +9,43 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 38) do
|
||||
ActiveRecord::Schema.define(:version => 20080617044632) do
|
||||
|
||||
create_table "contexts", :force => true do |t|
|
||||
t.string "name", :null => false
|
||||
t.integer "position"
|
||||
t.string "name", :default => "", :null => false
|
||||
t.integer "position", :limit => 11
|
||||
t.boolean "hide", :default => false
|
||||
t.integer "user_id", :default => 1
|
||||
t.integer "user_id", :limit => 11, :default => 1
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "contexts", ["user_id", "name"], :name => "index_contexts_on_user_id_and_name"
|
||||
add_index "contexts", ["user_id"], :name => "index_contexts_on_user_id"
|
||||
add_index "contexts", ["user_id", "name"], :name => "index_contexts_on_user_id_and_name"
|
||||
|
||||
create_table "notes", :force => true do |t|
|
||||
t.integer "user_id", :null => false
|
||||
t.integer "project_id", :null => false
|
||||
t.integer "user_id", :limit => 11, :null => false
|
||||
t.integer "project_id", :limit => 11, :null => false
|
||||
t.text "body"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "notes", ["user_id"], :name => "index_notes_on_user_id"
|
||||
add_index "notes", ["project_id"], :name => "index_notes_on_project_id"
|
||||
add_index "notes", ["user_id"], :name => "index_notes_on_user_id"
|
||||
|
||||
create_table "open_id_associations", :force => true do |t|
|
||||
t.binary "server_url"
|
||||
t.string "handle"
|
||||
t.binary "secret"
|
||||
t.integer "issued"
|
||||
t.integer "lifetime"
|
||||
t.integer "issued", :limit => 11
|
||||
t.integer "lifetime", :limit => 11
|
||||
t.string "assoc_type"
|
||||
end
|
||||
|
||||
create_table "open_id_nonces", :force => true do |t|
|
||||
t.string "nonce"
|
||||
t.integer "created"
|
||||
t.integer "created", :limit => 11
|
||||
end
|
||||
|
||||
create_table "open_id_settings", :force => true do |t|
|
||||
|
|
@ -54,40 +54,40 @@ ActiveRecord::Schema.define(:version => 38) do
|
|||
end
|
||||
|
||||
create_table "preferences", :force => true do |t|
|
||||
t.integer "user_id", :null => false
|
||||
t.integer "user_id", :limit => 11, :null => false
|
||||
t.string "date_format", :limit => 40, :default => "%d/%m/%Y", :null => false
|
||||
t.integer "week_starts", :default => 0, :null => false
|
||||
t.integer "show_number_completed", :default => 5, :null => false
|
||||
t.integer "staleness_starts", :default => 7, :null => false
|
||||
t.integer "week_starts", :limit => 11, :default => 0, :null => false
|
||||
t.integer "show_number_completed", :limit => 11, :default => 5, :null => false
|
||||
t.integer "staleness_starts", :limit => 11, :default => 7, :null => false
|
||||
t.boolean "show_completed_projects_in_sidebar", :default => true, :null => false
|
||||
t.boolean "show_hidden_contexts_in_sidebar", :default => true, :null => false
|
||||
t.integer "due_style", :default => 0, :null => false
|
||||
t.integer "due_style", :limit => 11, :default => 0, :null => false
|
||||
t.string "admin_email", :default => "butshesagirl@rousette.org.uk", :null => false
|
||||
t.integer "refresh", :default => 0, :null => false
|
||||
t.integer "refresh", :limit => 11, :default => 0, :null => false
|
||||
t.boolean "verbose_action_descriptors", :default => false, :null => false
|
||||
t.boolean "show_hidden_projects_in_sidebar", :default => true, :null => false
|
||||
t.string "time_zone", :default => "London", :null => false
|
||||
t.boolean "show_project_on_todo_done", :default => false, :null => false
|
||||
t.string "title_date_format", :default => "%A, %d %B %Y", :null => false
|
||||
t.integer "mobile_todos_per_page", :default => 6, :null => false
|
||||
t.integer "mobile_todos_per_page", :limit => 11, :default => 6, :null => false
|
||||
end
|
||||
|
||||
add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id"
|
||||
|
||||
create_table "projects", :force => true do |t|
|
||||
t.string "name", :null => false
|
||||
t.integer "position"
|
||||
t.integer "user_id", :default => 1
|
||||
t.string "name", :default => "", :null => false
|
||||
t.integer "position", :limit => 11
|
||||
t.integer "user_id", :limit => 11, :default => 1
|
||||
t.text "description"
|
||||
t.string "state", :limit => 20, :default => "active", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "default_context_id"
|
||||
t.integer "default_context_id", :limit => 11
|
||||
t.datetime "completed_at"
|
||||
end
|
||||
|
||||
add_index "projects", ["user_id", "name"], :name => "index_projects_on_user_id_and_name"
|
||||
add_index "projects", ["user_id"], :name => "index_projects_on_user_id"
|
||||
add_index "projects", ["user_id", "name"], :name => "index_projects_on_user_id_and_name"
|
||||
|
||||
create_table "sessions", :force => true do |t|
|
||||
t.string "session_id"
|
||||
|
|
@ -98,10 +98,10 @@ ActiveRecord::Schema.define(:version => 38) do
|
|||
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
|
||||
|
||||
create_table "taggings", :force => true do |t|
|
||||
t.integer "taggable_id"
|
||||
t.integer "tag_id"
|
||||
t.integer "taggable_id", :limit => 11
|
||||
t.integer "tag_id", :limit => 11
|
||||
t.string "taggable_type"
|
||||
t.integer "user_id"
|
||||
t.integer "user_id", :limit => 11
|
||||
end
|
||||
|
||||
add_index "taggings", ["tag_id", "taggable_id", "taggable_type"], :name => "index_taggings_on_tag_id_and_taggable_id_and_taggable_type"
|
||||
|
|
@ -115,27 +115,27 @@ ActiveRecord::Schema.define(:version => 38) do
|
|||
add_index "tags", ["name"], :name => "index_tags_on_name"
|
||||
|
||||
create_table "todos", :force => true do |t|
|
||||
t.integer "context_id", :null => false
|
||||
t.integer "project_id"
|
||||
t.string "description", :null => false
|
||||
t.integer "context_id", :limit => 11, :null => false
|
||||
t.integer "project_id", :limit => 11
|
||||
t.string "description", :default => "", :null => false
|
||||
t.text "notes"
|
||||
t.datetime "created_at"
|
||||
t.date "due"
|
||||
t.datetime "completed_at"
|
||||
t.integer "user_id", :default => 1
|
||||
t.integer "user_id", :limit => 11, :default => 1
|
||||
t.date "show_from"
|
||||
t.string "state", :limit => 20, :default => "immediate", :null => false
|
||||
end
|
||||
|
||||
add_index "todos", ["user_id", "context_id"], :name => "index_todos_on_user_id_and_context_id"
|
||||
add_index "todos", ["context_id"], :name => "index_todos_on_context_id"
|
||||
add_index "todos", ["project_id"], :name => "index_todos_on_project_id"
|
||||
add_index "todos", ["user_id", "project_id"], :name => "index_todos_on_user_id_and_project_id"
|
||||
add_index "todos", ["user_id", "state"], :name => "index_todos_on_user_id_and_state"
|
||||
add_index "todos", ["user_id", "project_id"], :name => "index_todos_on_user_id_and_project_id"
|
||||
add_index "todos", ["project_id"], :name => "index_todos_on_project_id"
|
||||
add_index "todos", ["context_id"], :name => "index_todos_on_context_id"
|
||||
add_index "todos", ["user_id", "context_id"], :name => "index_todos_on_user_id_and_context_id"
|
||||
|
||||
create_table "users", :force => true do |t|
|
||||
t.string "login", :limit => 80, :null => false
|
||||
t.string "crypted_password", :limit => 40, :null => false
|
||||
t.string "login", :limit => 80, :default => "", :null => false
|
||||
t.string "crypted_password", :limit => 40
|
||||
t.string "token"
|
||||
t.boolean "is_admin", :default => false, :null => false
|
||||
t.string "first_name"
|
||||
|
|
|
|||
|
|
@ -35,13 +35,14 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
|||
|
||||
<ol>
|
||||
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
|
||||
<li>Download using Subversion:</li>
|
||||
<li>If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):</li>
|
||||
</ol>
|
||||
|
||||
<pre>
|
||||
<code>
|
||||
svn co --username=guest
|
||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
||||
cd ~/Sites
|
||||
git clone git://github.com/bsag/tracks.git
|
||||
cd tracks
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
|
|
@ -189,13 +190,15 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
|||
|
||||
<ol>
|
||||
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
|
||||
<li>Download using Subversion:</li>
|
||||
<li>If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):</li>
|
||||
3.
|
||||
</ol>
|
||||
|
||||
<pre>
|
||||
<code>
|
||||
svn co --username=guest \
|
||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
||||
cd ~/Sites
|
||||
git clone git://github.com/bsag/tracks.git
|
||||
cd tracks
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
|
|
|
|||
|
|
@ -25,11 +25,13 @@ Tracks 1.6 has been thoroughly beta tested by a large number of people, and shou
|
|||
There are two methods of downloading Tracks 1.6:
|
||||
|
||||
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
|
||||
2. Download using Subversion:
|
||||
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
||||
|
||||
<pre>
|
||||
<code>
|
||||
svn co --username=guest
|
||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
||||
cd ~/Sites
|
||||
git clone git://github.com/bsag/tracks.git
|
||||
cd tracks
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
|
|
@ -162,11 +164,13 @@ Rename your old Tracks installation (e.g. to 'tracks-old') so that you can insta
|
|||
There are two methods of downloading Tracks 1.6:
|
||||
|
||||
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
|
||||
2. Download using Subversion:
|
||||
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
||||
3.
|
||||
<pre>
|
||||
<code>
|
||||
svn co --username=guest \
|
||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
||||
cd ~/Sites
|
||||
git clone git://github.com/bsag/tracks.git
|
||||
cd tracks
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
|
|
|
|||
BIN
doc/manual.pdf
BIN
doc/manual.pdf
Binary file not shown.
|
|
@ -112,12 +112,12 @@
|
|||
\usepackage{booktabs} % Better tables
|
||||
\usepackage{tabulary} % Support longer table cells
|
||||
\usepackage[utf8]{inputenc} % For UTF-8 support
|
||||
%\usepackage{xcolor} % Allow for color (annotations)
|
||||
|
||||
%\geometry{landscape} % Activate for rotated page geometry
|
||||
|
||||
%\usepackage[parfill]{parskip} % Activate to begin paragraphs with an empty
|
||||
% line rather than an indent
|
||||
% \usepackage{xcolor} % Allow for color (annotations)
|
||||
%
|
||||
% %\geometry{landscape} % Activate for rotated page geometry
|
||||
%
|
||||
% \usepackage[parfill]{parskip} % Activate to begin paragraphs with an empty
|
||||
% % line rather than an indent
|
||||
|
||||
|
||||
\def\myauthor{Author} % In case these were not included in metadata
|
||||
|
|
@ -136,8 +136,8 @@
|
|||
\def\revision{Revision: \$Id: manual.markdown 864 2008-06-03 17:01:00Z bsag \$}
|
||||
\def\mytitle{Tracks 1.6 Manual}
|
||||
\def\version{1.6}
|
||||
%%\usepackage{xmpincl}
|
||||
%%\includexmp{CCAttributionShareAlike}
|
||||
% \usepackage{xmpincl}
|
||||
% \includexmp{CCAttributionShareAlike}
|
||||
|
||||
|
||||
%
|
||||
|
|
@ -274,15 +274,16 @@ There are two methods of downloading Tracks 1.6:
|
|||
|
||||
\item (Recommended for most people) Download the \href{http://www.rousette.org.uk/projects/files/tracks-current.zip}{zipped package}, and unzip in your preferred location (e.g.\ \texttt{\ensuremath{\sim}/Sites} for Mac OS X users).
|
||||
|
||||
\item Download using Subversion:
|
||||
\item If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
||||
\end{enumerate}
|
||||
|
||||
\begin{adjustwidth}{2.5em}{2.5em}
|
||||
\begin{verbatim}
|
||||
|
||||
|
||||
svn co --username=guest
|
||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
||||
cd ~/Sites
|
||||
git clone git://github.com/bsag/tracks.git
|
||||
cd tracks
|
||||
|
||||
|
||||
\end{verbatim}
|
||||
|
|
@ -523,15 +524,16 @@ There are two methods of downloading Tracks 1.6:
|
|||
|
||||
\item (Recommended for most people) Download the \href{http://www.rousette.org.uk/projects/files/tracks-current.zip}{zipped package}, and unzip in your preferred location (e.g.\ \texttt{\ensuremath{\sim}/Sites} for Mac OS X users).
|
||||
|
||||
\item Download using Subversion:
|
||||
\item If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
||||
\end{enumerate}
|
||||
|
||||
\begin{adjustwidth}{2.5em}{2.5em}
|
||||
\begin{verbatim}
|
||||
|
||||
|
||||
svn co --username=guest \
|
||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
||||
cd ~/Sites
|
||||
git clone git://github.com/bsag/tracks.git
|
||||
cd tracks
|
||||
|
||||
|
||||
\end{verbatim}
|
||||
|
|
|
|||
2
public/javascripts/controls.js
vendored
2
public/javascripts/controls.js
vendored
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
||||
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
|
||||
// Contributors:
|
||||
|
|
|
|||
2
public/javascripts/dragdrop.js
vendored
2
public/javascripts/dragdrop.js
vendored
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
|
|
|
|||
2
public/javascripts/effects.js
vendored
2
public/javascripts/effects.js
vendored
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// Contributors:
|
||||
// Justin Palmer (http://encytemedia.com/)
|
||||
// Mark Pilgrim (http://diveintomark.org/)
|
||||
|
|
|
|||
3
script/dbconsole
Executable file
3
script/dbconsole
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/dbconsole'
|
||||
2
test/fixtures/projects.yml
vendored
2
test/fixtures/projects.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||
<%
|
||||
def today
|
||||
Time.now.utc.beginning_of_day.to_s(:db)
|
||||
Time.zone.now.beginning_of_day.to_s(:db)
|
||||
end
|
||||
%>
|
||||
|
||||
|
|
|
|||
10
test/fixtures/todos.yml
vendored
10
test/fixtures/todos.yml
vendored
|
|
@ -2,23 +2,23 @@
|
|||
<%
|
||||
|
||||
def today
|
||||
Time.now.utc.beginning_of_day.to_s(:db)
|
||||
Time.zone.now.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def next_week
|
||||
1.week.from_now.beginning_of_day.utc.to_s(:db)
|
||||
1.week.from_now.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def last_week
|
||||
1.week.ago.utc.beginning_of_day.to_s(:db)
|
||||
1.week.ago.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def two_weeks_ago
|
||||
2.weeks.ago.utc.beginning_of_day.to_s(:db)
|
||||
2.weeks.ago.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
def two_weeks_hence
|
||||
2.weeks.from_now.utc.beginning_of_day.to_s(:db)
|
||||
2.weeks.from_now.beginning_of_day.to_s(:db)
|
||||
end
|
||||
|
||||
%>
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class ContextsControllerTest < TodoContainerControllerTestBase
|
|||
assert_select 'p', /\d+ actions. Context is (Active|Hidden)./
|
||||
end
|
||||
end
|
||||
assert_select 'published', /(#{contexts(:agenda).created_at.xmlschema}|#{contexts(:library).created_at.xmlschema})/
|
||||
assert_select 'published', /(#{Regexp.escape(contexts(:agenda).created_at.xmlschema)}|#{Regexp.escape(contexts(:library).created_at.xmlschema)})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
|
|||
assert_select 'p', /\d+ actions. Project is (active|hidden|completed)./
|
||||
end
|
||||
end
|
||||
assert_select 'published', /(#{projects(:timemachine).updated_at.xmlschema}|#{projects(:moremoney).updated_at.xmlschema})/
|
||||
assert_select 'published', /(#{Regexp.escape(projects(:timemachine).updated_at.xmlschema)}|#{Regexp.escape(projects(:moremoney).updated_at.xmlschema)})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ class TodosControllerTest < Test::Rails::TestCase
|
|||
assert_xml_select 'entry', 10 do
|
||||
assert_xml_select 'title', /.+/
|
||||
assert_xml_select 'content[type="html"]', /.*/
|
||||
assert_xml_select 'published', /(#{projects(:timemachine).updated_at.xmlschema}|#{projects(:moremoney).updated_at.xmlschema})/
|
||||
assert_xml_select 'published', /(#{Regexp.escape(projects(:timemachine).updated_at.xmlschema)}|#{Regexp.escape(projects(:moremoney).updated_at.xmlschema)})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ require 'contexts_controller'
|
|||
class ContextsController; def rescue_action(e) raise e end; end
|
||||
|
||||
class ContextXmlApiTest < ActionController::IntegrationTest
|
||||
fixtures :users, :contexts
|
||||
fixtures :users, :contexts, :preferences
|
||||
|
||||
@@context_name = "@newcontext"
|
||||
@@valid_postdata = "<request><context><name>#{@@context_name}</name></context></request>"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ class SeleniumHelperController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
ActionController::Routing::Routes.add_route '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login'
|
||||
|
||||
module SeleniumOnRails::TestBuilderActions
|
||||
def login options = {}
|
||||
options = {options => nil} unless options.is_a? Hash
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ class PreferenceTest < Test::Rails::TestCase
|
|||
|
||||
def test_time_zone
|
||||
assert_equal 'London', @admin_user.preference.time_zone
|
||||
assert_equal @admin_user.preference.tz, TimeZone['London']
|
||||
end
|
||||
|
||||
def test_show_project_on_todo_done
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ class TodoTest < Test::Rails::TestCase
|
|||
assert_equal "Call Bill Gates to find out how much he makes per day", @not_completed1.description
|
||||
assert_nil @not_completed1.notes
|
||||
assert @not_completed1.completed? == false
|
||||
assert_equal 1.week.ago.utc.beginning_of_day.strftime("%Y-%m-%d %H:%M"), @not_completed1.created_at.strftime("%Y-%m-%d %H:%M")
|
||||
assert_equal 2.week.from_now.utc.beginning_of_day.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
|
||||
assert_equal 1.week.ago.beginning_of_day.strftime("%Y-%m-%d %H:%M"), @not_completed1.created_at.strftime("%Y-%m-%d %H:%M")
|
||||
assert_equal 2.week.from_now.beginning_of_day.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
|
||||
assert_nil @not_completed1.completed_at
|
||||
assert_equal 1, @not_completed1.user_id
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ class TodosHelperTest < Test::Rails::HelperTestCase
|
|||
include ApplicationHelper
|
||||
include TodosHelper
|
||||
|
||||
def user_time
|
||||
Time.now
|
||||
end
|
||||
|
||||
def format_date(date)
|
||||
if date
|
||||
date_format = "%d/%m/%Y"
|
||||
|
|
@ -31,7 +27,7 @@ class TodosHelperTest < Test::Rails::HelperTestCase
|
|||
end
|
||||
|
||||
def test_show_date_today
|
||||
date = Time.now.to_date
|
||||
date = Time.zone.now.to_date
|
||||
html = show_date(date)
|
||||
formatted_date = format_date(date)
|
||||
assert_equal %Q{<a title="#{formatted_date}"><span class="amber">Show Today</span></a> }, html
|
||||
|
|
|
|||
2
vendor/plugins/selenium-on-rails/init.rb
vendored
2
vendor/plugins/selenium-on-rails/init.rb
vendored
|
|
@ -7,6 +7,8 @@ if envs.include? RAILS_ENV
|
|||
require 'selenium_controller'
|
||||
require File.dirname(__FILE__) + '/routes'
|
||||
|
||||
SeleniumController.prepend_view_path File.expand_path(File.dirname(__FILE__) + '/lib/views')
|
||||
|
||||
else
|
||||
#erase all traces
|
||||
$LOAD_PATH.delete lib_path
|
||||
|
|
|
|||
|
|
@ -13,13 +13,8 @@ module SeleniumOnRails
|
|||
File.expand_path(File.dirname(__FILE__) + '/../views/' + view)
|
||||
end
|
||||
|
||||
# Returns the path to the layout template. The path is relative in relation
|
||||
# to the app/views/ directory since Rails doesn't support absolute paths
|
||||
# to layout templates.
|
||||
def layout_path
|
||||
rails_root = Pathname.new File.expand_path(File.join(RAILS_ROOT, 'app/views'))
|
||||
view_path = Pathname.new view_path('layout')
|
||||
view_path.relative_path_from(rails_root).to_s
|
||||
'/layout.rhtml'
|
||||
end
|
||||
|
||||
def fixtures_path
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
# See SeleniumOnRails::TestBuilder for a list of available commands.
|
||||
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
||||
end
|
||||
ActionView::Base.register_template_handler 'rsel', SeleniumOnRails::RSelenese
|
||||
ActionView::Template.register_template_handler 'rsel', SeleniumOnRails::RSelenese
|
||||
|
||||
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
||||
attr_accessor :view
|
||||
|
|
@ -20,16 +20,16 @@ class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
|||
end
|
||||
|
||||
# Render _template_ using _local_assigns_.
|
||||
def render template, local_assigns
|
||||
title = (@view.assigns['page_title'] or local_assigns['page_title'])
|
||||
def render template
|
||||
title = @view.assigns['page_title']
|
||||
table(title) do
|
||||
test = self #to enable test.command
|
||||
|
||||
assign_locals_code = ''
|
||||
local_assigns.each_key {|key| assign_locals_code << "#{key} = local_assigns[#{key.inspect}];"}
|
||||
|
||||
eval assign_locals_code + "\n" + template
|
||||
eval template.source
|
||||
end
|
||||
end
|
||||
|
||||
def compilable?
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
class SeleniumOnRails::Selenese
|
||||
end
|
||||
ActionView::Base.register_template_handler 'sel', SeleniumOnRails::Selenese
|
||||
ActionView::Template.register_template_handler 'sel', SeleniumOnRails::Selenese
|
||||
|
||||
|
||||
class SeleniumOnRails::Selenese
|
||||
|
|
@ -8,8 +8,8 @@ class SeleniumOnRails::Selenese
|
|||
@view = view
|
||||
end
|
||||
|
||||
def render template, local_assigns
|
||||
name = (@view.assigns['page_title'] or local_assigns['page_title'])
|
||||
def render template
|
||||
name = @view.assigns['page_title']
|
||||
lines = template.strip.split "\n"
|
||||
html = ''
|
||||
html << extract_comments(lines)
|
||||
|
|
@ -19,6 +19,10 @@ class SeleniumOnRails::Selenese
|
|||
html
|
||||
end
|
||||
|
||||
def compilable?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
def next_line lines, expects
|
||||
while lines.any?
|
||||
|
|
|
|||
4
vendor/plugins/will_paginate/.gitignore
vendored
4
vendor/plugins/will_paginate/.gitignore
vendored
|
|
@ -1,2 +1,4 @@
|
|||
/pkg
|
||||
/doc
|
||||
/rails
|
||||
*.gem
|
||||
/coverage
|
||||
|
|
|
|||
49
vendor/plugins/will_paginate/.manifest
vendored
Normal file
49
vendor/plugins/will_paginate/.manifest
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
CHANGELOG
|
||||
LICENSE
|
||||
README.rdoc
|
||||
Rakefile
|
||||
examples
|
||||
examples/apple-circle.gif
|
||||
examples/index.haml
|
||||
examples/index.html
|
||||
examples/pagination.css
|
||||
examples/pagination.sass
|
||||
init.rb
|
||||
lib
|
||||
lib/will_paginate
|
||||
lib/will_paginate.rb
|
||||
lib/will_paginate/array.rb
|
||||
lib/will_paginate/collection.rb
|
||||
lib/will_paginate/core_ext.rb
|
||||
lib/will_paginate/finder.rb
|
||||
lib/will_paginate/named_scope.rb
|
||||
lib/will_paginate/named_scope_patch.rb
|
||||
lib/will_paginate/version.rb
|
||||
lib/will_paginate/view_helpers.rb
|
||||
test
|
||||
test/boot.rb
|
||||
test/collection_test.rb
|
||||
test/console
|
||||
test/database.yml
|
||||
test/finder_test.rb
|
||||
test/fixtures
|
||||
test/fixtures/admin.rb
|
||||
test/fixtures/developer.rb
|
||||
test/fixtures/developers_projects.yml
|
||||
test/fixtures/project.rb
|
||||
test/fixtures/projects.yml
|
||||
test/fixtures/replies.yml
|
||||
test/fixtures/reply.rb
|
||||
test/fixtures/schema.rb
|
||||
test/fixtures/topic.rb
|
||||
test/fixtures/topics.yml
|
||||
test/fixtures/user.rb
|
||||
test/fixtures/users.yml
|
||||
test/helper.rb
|
||||
test/lib
|
||||
test/lib/activerecord_test_case.rb
|
||||
test/lib/activerecord_test_connector.rb
|
||||
test/lib/load_fixtures.rb
|
||||
test/lib/view_test_process.rb
|
||||
test/tasks.rake
|
||||
test/view_test.rb
|
||||
92
vendor/plugins/will_paginate/CHANGELOG
vendored
Normal file
92
vendor/plugins/will_paginate/CHANGELOG
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
== master
|
||||
|
||||
* ActiveRecord 2.1: remove :include from count query when tables are not
|
||||
referenced in :conditions
|
||||
|
||||
== 2.3.2, released 2008-05-16
|
||||
|
||||
* Fixed LinkRenderer#stringified_merge by removing "return" from iterator block
|
||||
* Ensure that 'href' values in pagination links are escaped URLs
|
||||
|
||||
== 2.3.1, released 2008-05-04
|
||||
|
||||
* Fixed page numbers not showing with custom routes and implicit first page
|
||||
* Try to use Hanna for documentation (falls back to default RDoc template if not)
|
||||
|
||||
== 2.3.0, released 2008-04-29
|
||||
|
||||
* Changed LinkRenderer to receive collection, options and reference to view template NOT in
|
||||
constructor, but with the #prepare method. This is a step towards supporting passing of
|
||||
LinkRenderer (or subclass) instances that may be preconfigured in some way
|
||||
* LinkRenderer now has #page_link and #page_span methods for easier customization of output in
|
||||
subclasses
|
||||
* Changed page_entries_info() method to adjust its output according to humanized class name of
|
||||
collection items. Override this with :entry_name parameter (singular).
|
||||
|
||||
page_entries_info(@posts)
|
||||
#-> "Displaying all 12 posts"
|
||||
page_entries_info(@posts, :entry_name => 'item')
|
||||
#-> "Displaying all 12 items"
|
||||
|
||||
== 2.2.3, released 2008-04-26
|
||||
|
||||
* will_paginate gem is no longer published on RubyForge, but on
|
||||
gems.github.com:
|
||||
|
||||
gem sources -a http://gems.github.com/ (you only need to do this once)
|
||||
gem install mislav-will_paginate
|
||||
|
||||
* extract reusable pagination testing stuff into WillPaginate::View
|
||||
* rethink the page URL construction mechanizm to be more bulletproof when
|
||||
combined with custom routing for page parameter
|
||||
* test that anchor parameter can be used in pagination links
|
||||
|
||||
== 2.2.2, released 2008-04-21
|
||||
|
||||
* Add support for page parameter in custom routes like "/foo/page/2"
|
||||
* Change output of "page_entries_info" on single-page collection and erraneous
|
||||
output with empty collection as reported by Tim Chater
|
||||
|
||||
== 2.2.1, released 2008-04-08
|
||||
|
||||
* take less risky path when monkeypatching named_scope; fix that it no longer
|
||||
requires ActiveRecord::VERSION
|
||||
* use strings in "respond_to?" calls to work around a bug in acts_as_ferret
|
||||
stable (ugh)
|
||||
* add rake release task
|
||||
|
||||
|
||||
== 2.2.0, released 2008-04-07
|
||||
|
||||
=== API changes
|
||||
* Rename WillPaginate::Collection#page_count to "total_pages" for consistency.
|
||||
If you implemented this interface, change your implementation accordingly.
|
||||
* Remove old, deprecated style of calling Array#paginate as "paginate(page,
|
||||
per_page)". If you want to specify :page, :per_page or :total_entries, use a
|
||||
parameter hash.
|
||||
* Rename LinkRenderer#url_options to "url_for" and drastically optimize it
|
||||
|
||||
=== View changes
|
||||
* Added "prev_page" and "next_page" CSS classes on previous/next page buttons
|
||||
* Add examples of pagination links styling in "examples/index.html"
|
||||
* Change gap in pagination links from "..." to
|
||||
"<span class="gap">…</span>".
|
||||
* Add "paginated_section", a block helper that renders pagination both above and
|
||||
below content in the block
|
||||
* Add rel="prev|next|start" to page links
|
||||
|
||||
=== Other
|
||||
|
||||
* Add ability to opt-in for Rails 2.1 feature "named_scope" by calling
|
||||
WillPaginate.enable_named_scope (tested in Rails 1.2.6 and 2.0.2)
|
||||
* Support complex page parameters like "developers[page]"
|
||||
* Move Array#paginate definition to will_paginate/array.rb. You can now easily
|
||||
use pagination on arrays outside of Rails:
|
||||
|
||||
gem 'will_paginate'
|
||||
require 'will_paginate/array'
|
||||
|
||||
* Add "paginated_each" method for iterating through every record by loading only
|
||||
one page of records at the time
|
||||
* Rails 2: Rescue from WillPaginate::InvalidPage error with 404 Not Found by
|
||||
default
|
||||
180
vendor/plugins/will_paginate/README
vendored
180
vendor/plugins/will_paginate/README
vendored
|
|
@ -1,180 +0,0 @@
|
|||
= WillPaginate
|
||||
|
||||
Pagination is just limiting the number of records displayed. Why should you let
|
||||
it get in your way while developing, then? This plugin makes magic happen. Did
|
||||
you ever want to be able to do just this on a model:
|
||||
|
||||
Post.paginate :page => 1, :order => 'created_at DESC'
|
||||
|
||||
... and then render the page links with a single view helper? Well, now you
|
||||
can.
|
||||
|
||||
Ryan Bates made an awesome screencast[http://railscasts.com/episodes/51],
|
||||
check it out.
|
||||
|
||||
Your mind reels with questions? Join our Google
|
||||
group[http://groups.google.com/group/will_paginate].
|
||||
|
||||
== Installation
|
||||
|
||||
Will Paginate officially supports Rails versions 1.2.6 and 2.0.x.
|
||||
|
||||
Previously, the plugin was available on the following SVN location:
|
||||
|
||||
svn://errtheblog.com/svn/plugins/will_paginate
|
||||
|
||||
In February 2008, it moved to GitHub[http://github.com/mislav/will_paginate/tree]
|
||||
to be tracked with git. The SVN repo continued to have updates, but not
|
||||
forever. Therefore you should switch to using the gem:
|
||||
|
||||
gem install will_paginate --no-ri
|
||||
|
||||
After that, you can remove the plugin from your applications and add
|
||||
a simple require to the end of config/environment.rb:
|
||||
|
||||
require 'will_paginate'
|
||||
|
||||
That's it, just remember to install the gem on all machines that
|
||||
you are deploying to.
|
||||
|
||||
The second option is to download and extract the tarball from GitHub. Here is the
|
||||
link for downloading the current state of the master branch:
|
||||
http://github.com/mislav/will_paginate/tarball/master
|
||||
|
||||
Extract it to <tt>vendor/plugins</tt>. The directory will have a default name
|
||||
like "mislav-will_paginate-master"; you can rename it to "will_paginate" for
|
||||
simplicity.
|
||||
|
||||
== Example usage
|
||||
|
||||
Use a paginate finder in the controller:
|
||||
|
||||
@posts = Post.paginate_by_board_id @board.id, :page => params[:page], :order => 'updated_at DESC'
|
||||
|
||||
Yeah, +paginate+ works just like +find+ -- it just doesn't fetch all the
|
||||
records. Don't forget to tell it which page you want, or it will complain!
|
||||
Read more on WillPaginate::Finder::ClassMethods.
|
||||
|
||||
Render the posts in your view like you would normally do. When you need to render
|
||||
pagination, just stick this in:
|
||||
|
||||
<%= will_paginate @posts %>
|
||||
|
||||
You're done. (Copy and paste the example fancy CSS styles from the bottom.) You
|
||||
can find the option list at WillPaginate::ViewHelpers.
|
||||
|
||||
How does it know how much items to fetch per page? It asks your model by calling
|
||||
its <tt>per_page</tt> class method. You can define it like this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
cattr_reader :per_page
|
||||
@@per_page = 50
|
||||
end
|
||||
|
||||
... or like this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
def self.per_page
|
||||
50
|
||||
end
|
||||
end
|
||||
|
||||
... or don't worry about it at all. WillPaginate defines it to be <b>30</b> by default.
|
||||
But you can always specify the count explicitly when calling +paginate+:
|
||||
|
||||
@posts = Post.paginate :page => params[:page], :per_page => 50
|
||||
|
||||
The +paginate+ finder wraps the original finder and returns your resultset that now has
|
||||
some new properties. You can use the collection as you would with any ActiveRecord
|
||||
resultset. WillPaginate view helpers also need that object to be able to render pagination:
|
||||
|
||||
<ol>
|
||||
<% for post in @posts -%>
|
||||
<li>Render `post` in some nice way.</li>
|
||||
<% end -%>
|
||||
</ol>
|
||||
|
||||
<p>Now let's render us some pagination!</p>
|
||||
<%= will_paginate @posts %>
|
||||
|
||||
More detailed documentation:
|
||||
|
||||
* WillPaginate::Finder::ClassMethods for pagination on your models;
|
||||
* WillPaginate::ViewHelpers for your views.
|
||||
|
||||
== Oh noes, a bug!
|
||||
|
||||
Tell us what happened so we can fix it, quick! Issues are filed on the Lighthouse project:
|
||||
http://err.lighthouseapp.com/projects/466-plugins/tickets?q=tagged:will_paginate
|
||||
|
||||
Steps to make an awesome bug report:
|
||||
|
||||
1. Run <tt>rake test</tt> in the <i>will_paginate</i> directory. (You will need SQLite3.)
|
||||
Copy the output if there are failing tests.
|
||||
2. Register on Lighthouse to create a new ticket.
|
||||
3. Write a descriptive, short title. Provide as much info as you can in the body.
|
||||
Assign the ticket to Mislav and tag it with meaningful tags, <tt>"will_paginate"</tt>
|
||||
being among them.
|
||||
4. Yay! You will be notified on updates automatically.
|
||||
|
||||
Here is an example of a great bug report and patch:
|
||||
http://err.lighthouseapp.com/projects/466/tickets/172-total_entries-ignored-in-paginate_by_sql
|
||||
|
||||
== Authors, credits, contact
|
||||
|
||||
Want to discuss, request features, ask questions? Join the Google group:
|
||||
http://groups.google.com/group/will_paginate
|
||||
|
||||
Authors:: Mislav Marohnić, PJ Hyett
|
||||
Original announcement:: http://errtheblog.com/post/929
|
||||
Original PHP source:: http://www.strangerstudios.com/sandbox/pagination/diggstyle.php
|
||||
|
||||
All these people helped making will_paginate what it is now with their code
|
||||
contributions or simply awesome ideas:
|
||||
|
||||
Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence
|
||||
Golda, Matt Aimonetti, Charles Brian Quinn, Desi McAdam, James Coglan, Matijs
|
||||
van Zuijlen, Maria, Brendan Ribera, Todd Willey, Bryan Helmkamp, Jan Berkel.
|
||||
|
||||
== Usable pagination in the UI
|
||||
|
||||
Copy the following CSS into your stylesheet for a good start:
|
||||
|
||||
.pagination {
|
||||
padding: 3px;
|
||||
margin: 3px;
|
||||
}
|
||||
.pagination a {
|
||||
padding: 2px 5px 2px 5px;
|
||||
margin: 2px;
|
||||
border: 1px solid #aaaadd;
|
||||
text-decoration: none;
|
||||
color: #000099;
|
||||
}
|
||||
.pagination a:hover, .pagination a:active {
|
||||
border: 1px solid #000099;
|
||||
color: #000;
|
||||
}
|
||||
.pagination span.current {
|
||||
padding: 2px 5px 2px 5px;
|
||||
margin: 2px;
|
||||
border: 1px solid #000099;
|
||||
font-weight: bold;
|
||||
background-color: #000099;
|
||||
color: #FFF;
|
||||
}
|
||||
.pagination span.disabled {
|
||||
padding: 2px 5px 2px 5px;
|
||||
margin: 2px;
|
||||
border: 1px solid #eee;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
More reading about pagination as design pattern:
|
||||
|
||||
* Pagination 101:
|
||||
http://kurafire.net/log/archive/2007/06/22/pagination-101
|
||||
* Pagination gallery:
|
||||
http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/
|
||||
* Pagination on Yahoo Design Pattern Library:
|
||||
http://developer.yahoo.com/ypatterns/parent.php?pattern=pagination
|
||||
131
vendor/plugins/will_paginate/README.rdoc
vendored
Normal file
131
vendor/plugins/will_paginate/README.rdoc
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
= WillPaginate
|
||||
|
||||
Pagination is just limiting the number of records displayed. Why should you let
|
||||
it get in your way while developing, then? This plugin makes magic happen. Did
|
||||
you ever want to be able to do just this on a model:
|
||||
|
||||
Post.paginate :page => 1, :order => 'created_at DESC'
|
||||
|
||||
... and then render the page links with a single view helper? Well, now you
|
||||
can.
|
||||
|
||||
Some resources to get you started:
|
||||
|
||||
* Your mind reels with questions? Join our
|
||||
{Google group}[http://groups.google.com/group/will_paginate].
|
||||
* The will_paginate project page: http://github.com/mislav/will_paginate
|
||||
* How to report bugs: http://github.com/mislav/will_paginate/wikis/report-bugs
|
||||
* Ryan Bates made an awesome screencast[http://railscasts.com/episodes/51],
|
||||
check it out.
|
||||
|
||||
== Installation
|
||||
|
||||
The recommended way is that you get the gem:
|
||||
|
||||
gem install mislav-will_paginate --source http://gems.github.com/
|
||||
|
||||
After that you don't need the will_paginate <i>plugin</i> in your Rails
|
||||
application anymore. Just add a simple require to the end of
|
||||
"config/environment.rb":
|
||||
|
||||
gem 'mislav-will_paginate', '~> 2.2'
|
||||
require 'will_paginate'
|
||||
|
||||
That's it. Remember to install the gem on <b>all</b> machines that you are
|
||||
deploying to.
|
||||
|
||||
<i>There are extensive
|
||||
{installation instructions}[http://github.com/mislav/will_paginate/wikis/installation]
|
||||
on {the wiki}[http://github.com/mislav/will_paginate/wikis].</i>
|
||||
|
||||
|
||||
== Example usage
|
||||
|
||||
Use a paginate finder in the controller:
|
||||
|
||||
@posts = Post.paginate_by_board_id @board.id, :page => params[:page], :order => 'updated_at DESC'
|
||||
|
||||
Yeah, +paginate+ works just like +find+ -- it just doesn't fetch all the
|
||||
records. Don't forget to tell it which page you want, or it will complain!
|
||||
Read more on WillPaginate::Finder::ClassMethods.
|
||||
|
||||
Render the posts in your view like you would normally do. When you need to render
|
||||
pagination, just stick this in:
|
||||
|
||||
<%= will_paginate @posts %>
|
||||
|
||||
You're done. (Copy and paste the example fancy CSS styles from the bottom.) You
|
||||
can find the option list at WillPaginate::ViewHelpers.
|
||||
|
||||
How does it know how much items to fetch per page? It asks your model by calling
|
||||
its <tt>per_page</tt> class method. You can define it like this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
cattr_reader :per_page
|
||||
@@per_page = 50
|
||||
end
|
||||
|
||||
... or like this:
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
def self.per_page
|
||||
50
|
||||
end
|
||||
end
|
||||
|
||||
... or don't worry about it at all. WillPaginate defines it to be <b>30</b> by default.
|
||||
But you can always specify the count explicitly when calling +paginate+:
|
||||
|
||||
@posts = Post.paginate :page => params[:page], :per_page => 50
|
||||
|
||||
The +paginate+ finder wraps the original finder and returns your resultset that now has
|
||||
some new properties. You can use the collection as you would with any ActiveRecord
|
||||
resultset. WillPaginate view helpers also need that object to be able to render pagination:
|
||||
|
||||
<ol>
|
||||
<% for post in @posts -%>
|
||||
<li>Render `post` in some nice way.</li>
|
||||
<% end -%>
|
||||
</ol>
|
||||
|
||||
<p>Now let's render us some pagination!</p>
|
||||
<%= will_paginate @posts %>
|
||||
|
||||
More detailed documentation:
|
||||
|
||||
* WillPaginate::Finder::ClassMethods for pagination on your models;
|
||||
* WillPaginate::ViewHelpers for your views.
|
||||
|
||||
|
||||
== Authors and credits
|
||||
|
||||
Authors:: Mislav Marohnić, PJ Hyett
|
||||
Original announcement:: http://errtheblog.com/post/929
|
||||
Original PHP source:: http://www.strangerstudios.com/sandbox/pagination/diggstyle.php
|
||||
|
||||
All these people helped making will_paginate what it is now with their code
|
||||
contributions or just simply awesome ideas:
|
||||
|
||||
Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence
|
||||
Golda, Matt Aimonetti, Charles Brian Quinn, Desi McAdam, James Coglan, Matijs
|
||||
van Zuijlen, Maria, Brendan Ribera, Todd Willey, Bryan Helmkamp, Jan Berkel,
|
||||
Lourens Naudé, Rick Olson, Russell Norris, Piotr Usewicz, Chris Eppstein.
|
||||
|
||||
|
||||
== Usable pagination in the UI
|
||||
|
||||
There are some CSS styles to get you started in the "examples/" directory. They
|
||||
are showcased in the <b>"examples/index.html"</b> file.
|
||||
|
||||
More reading about pagination as design pattern:
|
||||
|
||||
* Pagination 101:
|
||||
http://kurafire.net/log/archive/2007/06/22/pagination-101
|
||||
* Pagination gallery:
|
||||
http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/
|
||||
* Pagination on Yahoo Design Pattern Library:
|
||||
http://developer.yahoo.com/ypatterns/parent.php?pattern=pagination
|
||||
|
||||
Want to discuss, request features, ask questions? Join the
|
||||
{Google group}[http://groups.google.com/group/will_paginate].
|
||||
|
||||
107
vendor/plugins/will_paginate/Rakefile
vendored
107
vendor/plugins/will_paginate/Rakefile
vendored
|
|
@ -1,65 +1,62 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rubygems'
|
||||
begin
|
||||
hanna_dir = '/home/mislav/projects/hanna/lib'
|
||||
$:.unshift hanna_dir if File.exists? hanna_dir
|
||||
require 'hanna/rdoctask'
|
||||
rescue LoadError
|
||||
require 'rake'
|
||||
require 'rake/rdoctask'
|
||||
end
|
||||
load 'test/tasks.rake'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the will_paginate plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
# I want to specify environment variables at call time
|
||||
class EnvTestTask < Rake::TestTask
|
||||
attr_accessor :env
|
||||
|
||||
def ruby(*args)
|
||||
env.each { |key, value| ENV[key] = value } if env
|
||||
super
|
||||
env.keys.each { |key| ENV.delete key } if env
|
||||
end
|
||||
end
|
||||
|
||||
for configuration in %w( sqlite3 mysql postgres )
|
||||
EnvTestTask.new("test_#{configuration}") do |t|
|
||||
t.pattern = 'test/finder_test.rb'
|
||||
t.verbose = true
|
||||
t.env = { 'DB' => configuration }
|
||||
end
|
||||
end
|
||||
|
||||
task :test_databases => %w(test_mysql test_sqlite3 test_postgres)
|
||||
|
||||
desc %{Test everything on SQLite3, MySQL and PostgreSQL}
|
||||
task :test_full => %w(test test_mysql test_postgres)
|
||||
|
||||
desc %{Test everything with Rails 1.2.x and 2.0.x gems}
|
||||
task :test_all do
|
||||
all = Rake::Task['test_full']
|
||||
ENV['RAILS_VERSION'] = '~>1.2.6'
|
||||
all.invoke
|
||||
# reset the invoked flag
|
||||
%w( test_full test test_mysql test_postgres ).each do |name|
|
||||
Rake::Task[name].instance_variable_set '@already_invoked', false
|
||||
end
|
||||
# do it again
|
||||
ENV['RAILS_VERSION'] = '~>2.0.2'
|
||||
all.invoke
|
||||
end
|
||||
|
||||
desc 'Generate RDoc documentation for the will_paginate plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
files = ['README', 'LICENSE', 'lib/**/*.rb']
|
||||
rdoc.rdoc_files.add(files)
|
||||
rdoc.main = "README" # page to start on
|
||||
rdoc.title = "will_paginate"
|
||||
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG').
|
||||
include('lib/**/*.rb').
|
||||
exclude('lib/will_paginate/named_scope*').
|
||||
exclude('lib/will_paginate/array.rb').
|
||||
exclude('lib/will_paginate/version.rb')
|
||||
|
||||
templates = %w[/Users/chris/ruby/projects/err/rock/template.rb /var/www/rock/template.rb]
|
||||
rdoc.template = templates.find { |t| File.exists? t }
|
||||
rdoc.main = "README.rdoc" # page to start on
|
||||
rdoc.title = "will_paginate documentation"
|
||||
|
||||
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
||||
rdoc.options << '--inline-source'
|
||||
rdoc.options << '--charset=UTF-8'
|
||||
rdoc.options << '--inline-source' << '--charset=UTF-8'
|
||||
rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/'
|
||||
end
|
||||
|
||||
desc %{Update ".manifest" with the latest list of project filenames. Respect\
|
||||
.gitignore by excluding everything that git ignores. Update `files` and\
|
||||
`test_files` arrays in "*.gemspec" file if it's present.}
|
||||
task :manifest do
|
||||
list = Dir['**/*'].sort
|
||||
spec_file = Dir['*.gemspec'].first
|
||||
list -= [spec_file] if spec_file
|
||||
|
||||
File.read('.gitignore').each_line do |glob|
|
||||
glob = glob.chomp.sub(/^\//, '')
|
||||
list -= Dir[glob]
|
||||
list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
|
||||
puts "excluding #{glob}"
|
||||
end
|
||||
|
||||
if spec_file
|
||||
spec = File.read spec_file
|
||||
spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
|
||||
assignment = $1
|
||||
bunch = $2 ? list.grep(/^test\//) : list
|
||||
'%s%%w(%s)' % [assignment, bunch.join(' ')]
|
||||
end
|
||||
|
||||
File.open(spec_file, 'w') {|f| f << spec }
|
||||
end
|
||||
File.open('.manifest', 'w') {|f| f << list.join("\n") }
|
||||
end
|
||||
|
||||
task :examples do
|
||||
%x(haml examples/index.haml examples/index.html)
|
||||
%x(sass examples/pagination.sass examples/pagination.css)
|
||||
end
|
||||
|
|
|
|||
BIN
vendor/plugins/will_paginate/examples/apple-circle.gif
vendored
Normal file
BIN
vendor/plugins/will_paginate/examples/apple-circle.gif
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 178 B |
69
vendor/plugins/will_paginate/examples/index.haml
vendored
Normal file
69
vendor/plugins/will_paginate/examples/index.haml
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
!!!
|
||||
%html
|
||||
%head
|
||||
%title Samples of pagination styling for will_paginate
|
||||
%link{ :rel => 'stylesheet', :type => 'text/css', :href => 'pagination.css' }
|
||||
%style{ :type => 'text/css' }
|
||||
:sass
|
||||
html
|
||||
:margin 0
|
||||
:padding 0
|
||||
:background #999
|
||||
:font normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif
|
||||
body
|
||||
:margin 2em
|
||||
:padding 2em
|
||||
:border 2px solid gray
|
||||
:background white
|
||||
:color #222
|
||||
h1
|
||||
:font-size 2em
|
||||
:font-weight normal
|
||||
:margin 0 0 1em 0
|
||||
h2
|
||||
:font-size 1.4em
|
||||
:margin 1em 0 .5em 0
|
||||
pre
|
||||
:font-size 13px
|
||||
:font-family Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace
|
||||
|
||||
- pagination = '<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>'
|
||||
- pagination_no_page_links = '<span class="disabled prev_page">« Previous</span> <a href="./?page=2" rel="next" class="next_page">Next »</a>'
|
||||
|
||||
%body
|
||||
%h1 Samples of pagination styling for will_paginate
|
||||
%p
|
||||
Find these styles in <b>"examples/pagination.css"</b> of <i>will_paginate</i> library.
|
||||
There is a Sass version of it for all you sassy people.
|
||||
%p
|
||||
Read about good rules for pagination:
|
||||
%a{ :href => 'http://kurafire.net/log/archive/2007/06/22/pagination-101' } Pagination 101
|
||||
%p
|
||||
%em Warning:
|
||||
page links below don't lead anywhere (so don't click on them).
|
||||
|
||||
%h2 Unstyled pagination <span style="font-weight:normal">(<i>ewww!</i>)</span>
|
||||
%div= pagination
|
||||
|
||||
%h2 Digg.com
|
||||
.digg_pagination= pagination
|
||||
|
||||
%h2 Digg-style, no page links
|
||||
.digg_pagination= pagination_no_page_links
|
||||
%p Code that renders this:
|
||||
%pre= '<code>%s</code>' % %[<%= will_paginate @posts, :page_links => false %>].gsub('<', '<').gsub('>', '>')
|
||||
|
||||
%h2 Digg-style, extra content
|
||||
.digg_pagination
|
||||
.page_info Displaying entries <b>1 - 6</b> of <b>180</b> in total
|
||||
= pagination
|
||||
%p Code that renders this:
|
||||
%pre= '<code>%s</code>' % %[<div class="digg_pagination">\n <div clas="page_info">\n <%= page_entries_info @posts %>\n </div>\n <%= will_paginate @posts, :container => false %>\n</div>].gsub('<', '<').gsub('>', '>')
|
||||
|
||||
%h2 Apple.com store
|
||||
.apple_pagination= pagination
|
||||
|
||||
%h2 Flickr.com
|
||||
.flickr_pagination
|
||||
= pagination
|
||||
.page_info (118 photos)
|
||||
92
vendor/plugins/will_paginate/examples/index.html
vendored
Normal file
92
vendor/plugins/will_paginate/examples/index.html
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
</html>
|
||||
<head>
|
||||
<title>Samples of pagination styling for will_paginate</title>
|
||||
<link href='pagination.css' rel='stylesheet' type='text/css' />
|
||||
<style type='text/css'>
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #999;
|
||||
font: normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif; }
|
||||
|
||||
body {
|
||||
margin: 2em;
|
||||
padding: 2em;
|
||||
border: 2px solid gray;
|
||||
background: white;
|
||||
color: #222; }
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
font-weight: normal;
|
||||
margin: 0 0 1em 0; }
|
||||
|
||||
h2 {
|
||||
font-size: 1.4em;
|
||||
margin: 1em 0 .5em 0; }
|
||||
|
||||
pre {
|
||||
font-size: 13px;
|
||||
font-family: Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Samples of pagination styling for will_paginate</h1>
|
||||
<p>
|
||||
Find these styles in <b>"examples/pagination.css"</b> of <i>will_paginate</i> library.
|
||||
There is a Sass version of it for all you sassy people.
|
||||
</p>
|
||||
<p>
|
||||
Read about good rules for pagination:
|
||||
<a href='http://kurafire.net/log/archive/2007/06/22/pagination-101'>Pagination 101</a>
|
||||
</p>
|
||||
<p>
|
||||
<em>Warning:</em>
|
||||
page links below don't lead anywhere (so don't click on them).
|
||||
</p>
|
||||
<h2>
|
||||
Unstyled pagination <span style="font-weight:normal">(<i>ewww!</i>)</span>
|
||||
</h2>
|
||||
<div>
|
||||
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
||||
</div>
|
||||
<h2>Digg.com</h2>
|
||||
<div class='digg_pagination'>
|
||||
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
||||
</div>
|
||||
<h2>Digg-style, no page links</h2>
|
||||
<div class='digg_pagination'>
|
||||
<span class="disabled prev_page">« Previous</span> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
||||
</div>
|
||||
<p>Code that renders this:</p>
|
||||
<pre>
|
||||
<code><%= will_paginate @posts, :page_links => false %></code>
|
||||
</pre>
|
||||
<h2>Digg-style, extra content</h2>
|
||||
<div class='digg_pagination'>
|
||||
<div class='page_info'>
|
||||
Displaying entries <b>1 - 6</b> of <b>180</b> in total
|
||||
</div>
|
||||
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
||||
</div>
|
||||
<p>Code that renders this:</p>
|
||||
<pre>
|
||||
<code><div class="digg_pagination">
|
||||
<div clas="page_info">
|
||||
<%= page_entries_info @posts %>
|
||||
</div>
|
||||
<%= will_paginate @posts, :container => false %>
|
||||
</div></code>
|
||||
</pre>
|
||||
<h2>Apple.com store</h2>
|
||||
<div class='apple_pagination'>
|
||||
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
||||
</div>
|
||||
<h2>Flickr.com</h2>
|
||||
<div class='flickr_pagination'>
|
||||
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
||||
<div class='page_info'>(118 photos)</div>
|
||||
</div>
|
||||
</body>
|
||||
90
vendor/plugins/will_paginate/examples/pagination.css
vendored
Normal file
90
vendor/plugins/will_paginate/examples/pagination.css
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
.digg_pagination {
|
||||
background: white;
|
||||
/* self-clearing method: */ }
|
||||
.digg_pagination a, .digg_pagination span {
|
||||
padding: .2em .5em;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 1px; }
|
||||
.digg_pagination span.disabled {
|
||||
color: #999;
|
||||
border: 1px solid #DDD; }
|
||||
.digg_pagination span.current {
|
||||
font-weight: bold;
|
||||
background: #2E6AB1;
|
||||
color: white;
|
||||
border: 1px solid #2E6AB1; }
|
||||
.digg_pagination a {
|
||||
text-decoration: none;
|
||||
color: #105CB6;
|
||||
border: 1px solid #9AAFE5; }
|
||||
.digg_pagination a:hover, .digg_pagination a:focus {
|
||||
color: #003;
|
||||
border-color: #003; }
|
||||
.digg_pagination .page_info {
|
||||
background: #2E6AB1;
|
||||
color: white;
|
||||
padding: .4em .6em;
|
||||
width: 22em;
|
||||
margin-bottom: .3em;
|
||||
text-align: center; }
|
||||
.digg_pagination .page_info b {
|
||||
color: #003;
|
||||
background: #6aa6ed;
|
||||
padding: .1em .25em; }
|
||||
.digg_pagination:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden; }
|
||||
* html .digg_pagination {
|
||||
height: 1%; }
|
||||
*:first-child+html .digg_pagination {
|
||||
overflow: hidden; }
|
||||
|
||||
.apple_pagination {
|
||||
background: #F1F1F1;
|
||||
border: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
padding: 1em; }
|
||||
.apple_pagination a, .apple_pagination span {
|
||||
padding: .2em .3em; }
|
||||
.apple_pagination span.disabled {
|
||||
color: #AAA; }
|
||||
.apple_pagination span.current {
|
||||
font-weight: bold;
|
||||
background: transparent url(apple-circle.gif) no-repeat 50% 50%; }
|
||||
.apple_pagination a {
|
||||
text-decoration: none;
|
||||
color: black; }
|
||||
.apple_pagination a:hover, .apple_pagination a:focus {
|
||||
text-decoration: underline; }
|
||||
|
||||
.flickr_pagination {
|
||||
text-align: center;
|
||||
padding: .3em; }
|
||||
.flickr_pagination a, .flickr_pagination span {
|
||||
padding: .2em .5em; }
|
||||
.flickr_pagination span.disabled {
|
||||
color: #AAA; }
|
||||
.flickr_pagination span.current {
|
||||
font-weight: bold;
|
||||
color: #FF0084; }
|
||||
.flickr_pagination a {
|
||||
border: 1px solid #DDDDDD;
|
||||
color: #0063DC;
|
||||
text-decoration: none; }
|
||||
.flickr_pagination a:hover, .flickr_pagination a:focus {
|
||||
border-color: #003366;
|
||||
background: #0063DC;
|
||||
color: white; }
|
||||
.flickr_pagination .page_info {
|
||||
color: #aaa;
|
||||
padding-top: .8em; }
|
||||
.flickr_pagination .prev_page, .flickr_pagination .next_page {
|
||||
border-width: 2px; }
|
||||
.flickr_pagination .prev_page {
|
||||
margin-right: 1em; }
|
||||
.flickr_pagination .next_page {
|
||||
margin-left: 1em; }
|
||||
91
vendor/plugins/will_paginate/examples/pagination.sass
vendored
Normal file
91
vendor/plugins/will_paginate/examples/pagination.sass
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
.digg_pagination
|
||||
:background white
|
||||
a, span
|
||||
:padding .2em .5em
|
||||
:display block
|
||||
:float left
|
||||
:margin-right 1px
|
||||
span.disabled
|
||||
:color #999
|
||||
:border 1px solid #DDD
|
||||
span.current
|
||||
:font-weight bold
|
||||
:background #2E6AB1
|
||||
:color white
|
||||
:border 1px solid #2E6AB1
|
||||
a
|
||||
:text-decoration none
|
||||
:color #105CB6
|
||||
:border 1px solid #9AAFE5
|
||||
&:hover, &:focus
|
||||
:color #003
|
||||
:border-color #003
|
||||
.page_info
|
||||
:background #2E6AB1
|
||||
:color white
|
||||
:padding .4em .6em
|
||||
:width 22em
|
||||
:margin-bottom .3em
|
||||
:text-align center
|
||||
b
|
||||
:color #003
|
||||
:background = #2E6AB1 + 60
|
||||
:padding .1em .25em
|
||||
|
||||
/* self-clearing method:
|
||||
&:after
|
||||
:content "."
|
||||
:display block
|
||||
:height 0
|
||||
:clear both
|
||||
:visibility hidden
|
||||
* html &
|
||||
:height 1%
|
||||
*:first-child+html &
|
||||
:overflow hidden
|
||||
|
||||
.apple_pagination
|
||||
:background #F1F1F1
|
||||
:border 1px solid #E5E5E5
|
||||
:text-align center
|
||||
:padding 1em
|
||||
a, span
|
||||
:padding .2em .3em
|
||||
span.disabled
|
||||
:color #AAA
|
||||
span.current
|
||||
:font-weight bold
|
||||
:background transparent url(apple-circle.gif) no-repeat 50% 50%
|
||||
a
|
||||
:text-decoration none
|
||||
:color black
|
||||
&:hover, &:focus
|
||||
:text-decoration underline
|
||||
|
||||
.flickr_pagination
|
||||
:text-align center
|
||||
:padding .3em
|
||||
a, span
|
||||
:padding .2em .5em
|
||||
span.disabled
|
||||
:color #AAA
|
||||
span.current
|
||||
:font-weight bold
|
||||
:color #FF0084
|
||||
a
|
||||
:border 1px solid #DDDDDD
|
||||
:color #0063DC
|
||||
:text-decoration none
|
||||
&:hover, &:focus
|
||||
:border-color #003366
|
||||
:background #0063DC
|
||||
:color white
|
||||
.page_info
|
||||
:color #aaa
|
||||
:padding-top .8em
|
||||
.prev_page, .next_page
|
||||
:border-width 2px
|
||||
.prev_page
|
||||
:margin-right 1em
|
||||
.next_page
|
||||
:margin-left 1em
|
||||
1
vendor/plugins/will_paginate/init.rb
vendored
1
vendor/plugins/will_paginate/init.rb
vendored
|
|
@ -1,2 +1 @@
|
|||
require 'will_paginate'
|
||||
WillPaginate.enable
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module WillPaginate
|
|||
require 'will_paginate/view_helpers'
|
||||
ActionView::Base.class_eval { include ViewHelpers }
|
||||
|
||||
if ActionController::Base.respond_to? :rescue_responses
|
||||
if defined?(ActionController::Base) and ActionController::Base.respond_to? :rescue_responses
|
||||
ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
|
||||
end
|
||||
end
|
||||
|
|
@ -33,28 +33,45 @@ module WillPaginate
|
|||
require 'will_paginate/finder'
|
||||
ActiveRecord::Base.class_eval { include Finder }
|
||||
|
||||
associations = ActiveRecord::Associations
|
||||
collection = associations::AssociationCollection
|
||||
|
||||
# to support paginating finders on associations, we have to mix in the
|
||||
# method_missing magic from WillPaginate::Finder::ClassMethods to AssociationProxy
|
||||
# subclasses, but in a different way for Rails 1.2.x and 2.0
|
||||
(collection.instance_methods.include?(:create!) ?
|
||||
collection : collection.subclasses.map(&:constantize)
|
||||
).push(associations::HasManyThroughAssociation).each do |klass|
|
||||
# support pagination on associations
|
||||
a = ActiveRecord::Associations
|
||||
returning([ a::AssociationCollection ]) { |classes|
|
||||
# detect http://dev.rubyonrails.org/changeset/9230
|
||||
unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
|
||||
classes << a::HasManyThroughAssociation
|
||||
end
|
||||
}.each do |klass|
|
||||
klass.class_eval do
|
||||
include Finder::ClassMethods
|
||||
alias_method_chain :method_missing, :paginate
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Enable named_scope, a feature of Rails 2.1, even if you have older Rails
|
||||
# (tested on Rails 2.0.2 and 1.2.6).
|
||||
#
|
||||
# You can pass +false+ for +patch+ parameter to skip monkeypatching
|
||||
# *associations*. Use this if you feel that <tt>named_scope</tt> broke
|
||||
# has_many, has_many :through or has_and_belongs_to_many associations in
|
||||
# your app. By passing +false+, you can still use <tt>named_scope</tt> in
|
||||
# your models, but not through associations.
|
||||
def enable_named_scope(patch = true)
|
||||
return if defined? ActiveRecord::NamedScope
|
||||
require 'will_paginate/named_scope'
|
||||
require 'will_paginate/named_scope_patch' if patch
|
||||
|
||||
ActiveRecord::Base.class_eval do
|
||||
include WillPaginate::NamedScope
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Deprecation #:nodoc:
|
||||
extend ActiveSupport::Deprecation
|
||||
|
||||
def self.warn(message, callstack = caller)
|
||||
message = 'WillPaginate: ' + message.strip.gsub(/ {3,}/, ' ')
|
||||
message = 'WillPaginate: ' + message.strip.gsub(/\s+/, ' ')
|
||||
behavior.call(message, callstack) if behavior && !silenced?
|
||||
end
|
||||
|
||||
|
|
@ -63,3 +80,7 @@ module WillPaginate
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
if defined?(Rails) and defined?(ActiveRecord) and defined?(ActionController)
|
||||
WillPaginate.enable
|
||||
end
|
||||
|
|
|
|||
16
vendor/plugins/will_paginate/lib/will_paginate/array.rb
vendored
Normal file
16
vendor/plugins/will_paginate/lib/will_paginate/array.rb
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
require 'will_paginate/collection'
|
||||
|
||||
# http://www.desimcadam.com/archives/8
|
||||
Array.class_eval do
|
||||
def paginate(options = {})
|
||||
raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
|
||||
|
||||
WillPaginate::Collection.create(
|
||||
options[:page] || 1,
|
||||
options[:per_page] || 30,
|
||||
options[:total_entries] || self.length
|
||||
) { |pager|
|
||||
pager.replace self[pager.offset, pager.per_page].to_a
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
require 'will_paginate'
|
||||
|
||||
module WillPaginate
|
||||
# = OMG, invalid page number!
|
||||
# = Invalid page number error
|
||||
# This is an ArgumentError raised in case a page was requested that is either
|
||||
# zero or negative number. You should decide how do deal with such errors in
|
||||
# the controller.
|
||||
#
|
||||
# If you're using Rails 2, then this error will automatically get handled like
|
||||
# 404 Not Found. The hook is in "will_paginate.rb":
|
||||
#
|
||||
# ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
|
||||
#
|
||||
# If you don't like this, use your preffered method of rescuing exceptions in
|
||||
# public from your controllers to handle this differently. The +rescue_from+
|
||||
# method is a nice addition to Rails 2.
|
||||
#
|
||||
# This error is *not* raised when a page further than the last page is
|
||||
# requested. Use <tt>WillPaginate::Collection#out_of_bounds?</tt> method to
|
||||
# check for those cases and manually deal with them as you see fit.
|
||||
|
|
@ -15,22 +22,30 @@ module WillPaginate
|
|||
end
|
||||
end
|
||||
|
||||
# Arrays returned from paginating finds are, in fact, instances of this.
|
||||
# You may think of WillPaginate::Collection as an ordinary array with some
|
||||
# extra properties. Those properties are used by view helpers to generate
|
||||
# = The key to pagination
|
||||
# Arrays returned from paginating finds are, in fact, instances of this little
|
||||
# class. You may think of WillPaginate::Collection as an ordinary array with
|
||||
# some extra properties. Those properties are used by view helpers to generate
|
||||
# correct page links.
|
||||
#
|
||||
# WillPaginate::Collection also assists in rolling out your own pagination
|
||||
# solutions: see +create+.
|
||||
#
|
||||
# If you are writing a library that provides a collection which you would like
|
||||
# to conform to this API, you don't have to copy these methods over; simply
|
||||
# make your plugin/gem dependant on the "will_paginate" gem:
|
||||
#
|
||||
# gem 'will_paginate'
|
||||
# require 'will_paginate/collection'
|
||||
#
|
||||
# # now use WillPaginate::Collection directly or subclass it
|
||||
class Collection < Array
|
||||
attr_reader :current_page, :per_page, :total_entries
|
||||
attr_reader :current_page, :per_page, :total_entries, :total_pages
|
||||
|
||||
# Arguments to this constructor are the current page number, per-page limit
|
||||
# Arguments to the constructor are the current page number, per-page limit
|
||||
# and the total number of entries. The last argument is optional because it
|
||||
# is best to do lazy counting; in other words, count *conditionally* after
|
||||
# populating the collection using the +replace+ method.
|
||||
#
|
||||
def initialize(page, per_page, total = nil)
|
||||
@current_page = page.to_i
|
||||
raise InvalidPage.new(page, @current_page) if @current_page < 1
|
||||
|
|
@ -65,29 +80,25 @@ module WillPaginate
|
|||
# end
|
||||
# end
|
||||
#
|
||||
# The Array#paginate API has since then changed, but this still serves as a
|
||||
# fine example of WillPaginate::Collection usage.
|
||||
def self.create(page, per_page, total = nil, &block)
|
||||
pager = new(page, per_page, total)
|
||||
yield pager
|
||||
pager
|
||||
end
|
||||
|
||||
# The total number of pages.
|
||||
def page_count
|
||||
@total_pages
|
||||
end
|
||||
|
||||
# Helper method that is true when someone tries to fetch a page with a
|
||||
# larger number than the last page. Can be used in combination with flashes
|
||||
# and redirecting.
|
||||
def out_of_bounds?
|
||||
current_page > page_count
|
||||
current_page > total_pages
|
||||
end
|
||||
|
||||
# Current offset of the paginated collection. If we're on the first page,
|
||||
# it is always 0. If we're on the 2nd page and there are 30 entries per page,
|
||||
# the offset is 30. This property is useful if you want to render ordinals
|
||||
# besides your records: simply start with offset + 1.
|
||||
#
|
||||
def offset
|
||||
(current_page - 1) * per_page
|
||||
end
|
||||
|
|
@ -99,7 +110,7 @@ module WillPaginate
|
|||
|
||||
# current_page + 1 or nil if there is no next page
|
||||
def next_page
|
||||
current_page < page_count ? (current_page + 1) : nil
|
||||
current_page < total_pages ? (current_page + 1) : nil
|
||||
end
|
||||
|
||||
def total_entries=(number)
|
||||
|
|
@ -120,13 +131,15 @@ module WillPaginate
|
|||
# +total_entries+ and set it to a proper value if it's +nil+. See the example
|
||||
# in +create+.
|
||||
def replace(array)
|
||||
returning super do
|
||||
result = super
|
||||
|
||||
# The collection is shorter then page limit? Rejoice, because
|
||||
# then we know that we are on the last page!
|
||||
if total_entries.nil? and length > 0 and length < per_page
|
||||
if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
|
||||
self.total_entries = offset + length
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
require 'will_paginate'
|
||||
require 'set'
|
||||
require 'will_paginate/array'
|
||||
|
||||
unless Hash.instance_methods.include? 'except'
|
||||
Hash.class_eval do
|
||||
|
|
@ -30,51 +30,3 @@ unless Hash.instance_methods.include? 'slice'
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless Hash.instance_methods.include? 'rec_merge!'
|
||||
Hash.class_eval do
|
||||
# Same as Hash#merge!, but recursively merges sub-hashes
|
||||
# (stolen from Haml)
|
||||
def rec_merge!(other)
|
||||
other.each do |key, other_value|
|
||||
value = self[key]
|
||||
if value.is_a?(Hash) and other_value.is_a?(Hash)
|
||||
value.rec_merge! other_value
|
||||
else
|
||||
self[key] = other_value
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'will_paginate/collection'
|
||||
|
||||
unless Array.instance_methods.include? 'paginate'
|
||||
# http://www.desimcadam.com/archives/8
|
||||
Array.class_eval do
|
||||
def paginate(options_or_page = {}, per_page = nil)
|
||||
if options_or_page.nil? or Fixnum === options_or_page
|
||||
if defined? WillPaginate::Deprecation
|
||||
WillPaginate::Deprecation.warn <<-DEPR
|
||||
Array#paginate now conforms to the main, ActiveRecord::Base#paginate API. You should \
|
||||
call it with a parameters hash (:page, :per_page). The old API (numbers as arguments) \
|
||||
has been deprecated and is going to be unsupported in future versions of will_paginate.
|
||||
DEPR
|
||||
end
|
||||
page = options_or_page
|
||||
options = {}
|
||||
else
|
||||
options = options_or_page
|
||||
page = options[:page]
|
||||
raise ArgumentError, "wrong number of arguments (1 hash or 2 Fixnums expected)" if per_page
|
||||
per_page = options[:per_page]
|
||||
end
|
||||
|
||||
WillPaginate::Collection.create(page || 1, per_page || 30, options[:total_entries] || size) do |pager|
|
||||
pager.replace self[pager.offset, pager.per_page].to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ require 'will_paginate/core_ext'
|
|||
|
||||
module WillPaginate
|
||||
# A mixin for ActiveRecord::Base. Provides +per_page+ class method
|
||||
# and makes +paginate+ finders possible with some method_missing magic.
|
||||
# and hooks things up to provide paginating finders.
|
||||
#
|
||||
# Find out more in WillPaginate::Finder::ClassMethods
|
||||
#
|
||||
|
|
@ -18,9 +18,9 @@ module WillPaginate
|
|||
|
||||
# = Paginating finders for ActiveRecord models
|
||||
#
|
||||
# WillPaginate adds +paginate+ and +per_page+ methods to ActiveRecord::Base
|
||||
# class methods and associations. It also hooks into +method_missing+ to
|
||||
# intercept pagination calls to dynamic finders such as
|
||||
# WillPaginate adds +paginate+, +per_page+ and other methods to
|
||||
# ActiveRecord::Base class methods and associations. It also hooks into
|
||||
# +method_missing+ to intercept pagination calls to dynamic finders such as
|
||||
# +paginate_by_user_id+ and translate them to ordinary finders
|
||||
# (+find_all_by_user_id+ in this case).
|
||||
#
|
||||
|
|
@ -86,6 +86,31 @@ module WillPaginate
|
|||
end
|
||||
end
|
||||
|
||||
# Iterates through all records by loading one page at a time. This is useful
|
||||
# for migrations or any other use case where you don't want to load all the
|
||||
# records in memory at once.
|
||||
#
|
||||
# It uses +paginate+ internally; therefore it accepts all of its options.
|
||||
# You can specify a starting page with <tt>:page</tt> (default is 1). Default
|
||||
# <tt>:order</tt> is <tt>"id"</tt>, override if necessary.
|
||||
#
|
||||
# See http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord where
|
||||
# Jamis Buck describes this and also uses a more efficient way for MySQL.
|
||||
def paginated_each(options = {}, &block)
|
||||
options = { :order => 'id', :page => 1 }.merge options
|
||||
options[:page] = options[:page].to_i
|
||||
options[:total_entries] = 0 # skip the individual count queries
|
||||
total = 0
|
||||
|
||||
begin
|
||||
collection = paginate(options)
|
||||
total += collection.each(&block).size
|
||||
options[:page] += 1
|
||||
end until collection.size < collection.per_page
|
||||
|
||||
total
|
||||
end
|
||||
|
||||
# Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
|
||||
# based on the params otherwise used by paginating finds: +page+ and
|
||||
# +per_page+.
|
||||
|
|
@ -159,6 +184,7 @@ module WillPaginate
|
|||
unless options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
|
||||
excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
|
||||
end
|
||||
|
||||
# count expects (almost) the same options as find
|
||||
count_options = options.except *excludees
|
||||
|
||||
|
|
@ -166,12 +192,19 @@ module WillPaginate
|
|||
# this allows you to specify :select, :order, or anything else just for the count query
|
||||
count_options.update options[:count] if options[:count]
|
||||
|
||||
# we may be in a model or an association proxy
|
||||
klass = (@owner and @reflection) ? @reflection.klass : self
|
||||
|
||||
# forget about includes if they are irrelevant (Rails 2.1)
|
||||
if count_options[:include] and
|
||||
klass.private_methods.include?('references_eager_loaded_tables?') and
|
||||
!klass.send(:references_eager_loaded_tables?, count_options)
|
||||
count_options.delete :include
|
||||
end
|
||||
|
||||
# we may have to scope ...
|
||||
counter = Proc.new { count(count_options) }
|
||||
|
||||
# we may be in a model or an association proxy!
|
||||
klass = (@owner and @reflection) ? @reflection.klass : self
|
||||
|
||||
count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
|
||||
# scope_out adds a 'with_finder' method which acts like with_scope, if it's present
|
||||
# then execute the count with the scoping provided by the with_finder
|
||||
|
|
|
|||
132
vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb
vendored
Normal file
132
vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
## stolen from: http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/named_scope.rb?rev=9084
|
||||
|
||||
module WillPaginate
|
||||
# This is a feature backported from Rails 2.1 because of its usefullness not only with will_paginate,
|
||||
# but in other aspects when managing complex conditions that you want to be reusable.
|
||||
module NamedScope
|
||||
# All subclasses of ActiveRecord::Base have two named_scopes:
|
||||
# * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
|
||||
# * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly:
|
||||
#
|
||||
# Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)
|
||||
#
|
||||
# These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
|
||||
# intermediate values (scopes) around as first-class objects is convenient.
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
extend ClassMethods
|
||||
named_scope :all
|
||||
named_scope :scoped, lambda { |scope| scope }
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def scopes #:nodoc:
|
||||
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
|
||||
end
|
||||
|
||||
# Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
|
||||
# such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
|
||||
#
|
||||
# class Shirt < ActiveRecord::Base
|
||||
# named_scope :red, :conditions => {:color => 'red'}
|
||||
# named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
|
||||
# end
|
||||
#
|
||||
# The above calls to <tt>named_scope</tt> define class methods <tt>Shirt.red</tt> and <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>,
|
||||
# in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
|
||||
#
|
||||
# Unlike Shirt.find(...), however, the object returned by <tt>Shirt.red</tt> is not an Array; it resembles the association object
|
||||
# constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
|
||||
# <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
|
||||
# as with the association objects, name scopes acts like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
|
||||
# <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really were an Array.
|
||||
#
|
||||
# These named scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
|
||||
# Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
|
||||
# for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
|
||||
#
|
||||
# All scopes are available as class methods on the ActiveRecord descendent upon which the scopes were defined. But they are also available to
|
||||
# <tt>has_many</tt> associations. If,
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# has_many :shirts
|
||||
# end
|
||||
#
|
||||
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
|
||||
# only shirts.
|
||||
#
|
||||
# Named scopes can also be procedural.
|
||||
#
|
||||
# class Shirt < ActiveRecord::Base
|
||||
# named_scope :colored, lambda { |color|
|
||||
# { :conditions => { :color => color } }
|
||||
# }
|
||||
# end
|
||||
#
|
||||
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
|
||||
#
|
||||
# Named scopes can also have extensions, just as with <tt>has_many</tt> declarations:
|
||||
#
|
||||
# class Shirt < ActiveRecord::Base
|
||||
# named_scope :red, :conditions => {:color => 'red'} do
|
||||
# def dom_id
|
||||
# 'red_shirts'
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def named_scope(name, options = {}, &block)
|
||||
scopes[name] = lambda do |parent_scope, *args|
|
||||
Scope.new(parent_scope, case options
|
||||
when Hash
|
||||
options
|
||||
when Proc
|
||||
options.call(*args)
|
||||
end, &block)
|
||||
end
|
||||
(class << self; self end).instance_eval do
|
||||
define_method name do |*args|
|
||||
scopes[name].call(self, *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Scope #:nodoc:
|
||||
attr_reader :proxy_scope, :proxy_options
|
||||
[].methods.each { |m| delegate m, :to => :proxy_found unless m =~ /(^__|^nil\?|^send|class|extend|find|count|sum|average|maximum|minimum|paginate)/ }
|
||||
delegate :scopes, :with_scope, :to => :proxy_scope
|
||||
|
||||
def initialize(proxy_scope, options, &block)
|
||||
[options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
|
||||
extend Module.new(&block) if block_given?
|
||||
@proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
|
||||
end
|
||||
|
||||
def reload
|
||||
load_found; self
|
||||
end
|
||||
|
||||
protected
|
||||
def proxy_found
|
||||
@found || load_found
|
||||
end
|
||||
|
||||
private
|
||||
def method_missing(method, *args, &block)
|
||||
if scopes.include?(method)
|
||||
scopes[method].call(self, *args)
|
||||
else
|
||||
with_scope :find => proxy_options do
|
||||
proxy_scope.send(method, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load_found
|
||||
@found = find(:all)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
39
vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb
vendored
Normal file
39
vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
## based on http://dev.rubyonrails.org/changeset/9084
|
||||
|
||||
ActiveRecord::Associations::AssociationProxy.class_eval do
|
||||
protected
|
||||
def with_scope(*args, &block)
|
||||
@reflection.klass.send :with_scope, *args, &block
|
||||
end
|
||||
end
|
||||
|
||||
[ ActiveRecord::Associations::AssociationCollection,
|
||||
ActiveRecord::Associations::HasManyThroughAssociation ].each do |klass|
|
||||
klass.class_eval do
|
||||
protected
|
||||
alias :method_missing_without_scopes :method_missing_without_paginate
|
||||
def method_missing_without_paginate(method, *args, &block)
|
||||
if @reflection.klass.scopes.include?(method)
|
||||
@reflection.klass.scopes[method].call(self, *args, &block)
|
||||
else
|
||||
method_missing_without_scopes(method, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Rails 1.2.6
|
||||
ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
|
||||
protected
|
||||
def method_missing(method, *args, &block)
|
||||
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
||||
super
|
||||
elsif @reflection.klass.scopes.include?(method)
|
||||
@reflection.klass.scopes[method].call(self, *args)
|
||||
else
|
||||
@reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
|
||||
@reflection.klass.send(method, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end if ActiveRecord::Base.respond_to? :find_first
|
||||
9
vendor/plugins/will_paginate/lib/will_paginate/version.rb
vendored
Normal file
9
vendor/plugins/will_paginate/lib/will_paginate/version.rb
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
module WillPaginate
|
||||
module VERSION
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 3
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
|
|
@ -49,12 +49,13 @@ module WillPaginate
|
|||
# * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
|
||||
# * <tt>:params</tt> -- additional parameters when generating pagination links
|
||||
# (eg. <tt>:controller => "foo", :action => nil</tt>)
|
||||
# * <tt>:renderer</tt> -- class name of the link renderer (default: WillPaginate::LinkRenderer)
|
||||
# * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
|
||||
# <tt>WillPaginate::LinkRenderer</tt>)
|
||||
# * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
|
||||
# * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
|
||||
# false only when you are rendering your own pagination markup (default: true)
|
||||
# * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID automatically
|
||||
# generated from the class name of objects in collection: for example, paginating
|
||||
# * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID
|
||||
# automatically generated from the class name of objects in collection: for example, paginating
|
||||
# ArticleComment models would yield an ID of "article_comments_pagination".
|
||||
#
|
||||
# All options beside listed ones are passed as HTML attributes to the container
|
||||
|
|
@ -85,25 +86,79 @@ module WillPaginate
|
|||
collection_name = "@#{controller.controller_name}"
|
||||
collection = instance_variable_get(collection_name)
|
||||
raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
|
||||
"forget to specify the collection object for will_paginate?" unless collection
|
||||
"forget to pass the collection object for will_paginate?" unless collection
|
||||
end
|
||||
# early exit if there is nothing to render
|
||||
return nil unless collection.page_count > 1
|
||||
return nil unless WillPaginate::ViewHelpers.total_pages_for_collection(collection) > 1
|
||||
|
||||
options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options
|
||||
# create the renderer instance
|
||||
renderer_class = options[:renderer].to_s.constantize
|
||||
renderer = renderer_class.new collection, options, self
|
||||
|
||||
# get the renderer instance
|
||||
renderer = case options[:renderer]
|
||||
when String
|
||||
options[:renderer].to_s.constantize.new
|
||||
when Class
|
||||
options[:renderer].new
|
||||
else
|
||||
options[:renderer]
|
||||
end
|
||||
# render HTML for pagination
|
||||
renderer.prepare collection, options, self
|
||||
renderer.to_html
|
||||
end
|
||||
|
||||
# Wrapper for rendering pagination links at both top and bottom of a block
|
||||
# of content.
|
||||
#
|
||||
# <% paginated_section @posts do %>
|
||||
# <ol id="posts">
|
||||
# <% for post in @posts %>
|
||||
# <li> ... </li>
|
||||
# <% end %>
|
||||
# </ol>
|
||||
# <% end %>
|
||||
#
|
||||
# will result in:
|
||||
#
|
||||
# <div class="pagination"> ... </div>
|
||||
# <ol id="posts">
|
||||
# ...
|
||||
# </ol>
|
||||
# <div class="pagination"> ... </div>
|
||||
#
|
||||
# Arguments are passed to a <tt>will_paginate</tt> call, so the same options
|
||||
# apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
|
||||
# blocks of pagination links sharing the same ID (which is invalid HTML).
|
||||
def paginated_section(*args, &block)
|
||||
pagination = will_paginate(*args).to_s
|
||||
content = pagination + capture(&block) + pagination
|
||||
concat content, block.binding
|
||||
end
|
||||
|
||||
# Renders a helpful message with numbers of displayed vs. total entries.
|
||||
# You can use this as a blueprint for your own, similar helpers.
|
||||
#
|
||||
# <%= page_entries_info @posts %>
|
||||
# #-> Displaying entries 6 - 10 of 26 in total
|
||||
def page_entries_info(collection)
|
||||
%{Displaying entries <b>%d - %d</b> of <b>%d</b> in total} % [
|
||||
# #-> Displaying posts 6 - 10 of 26 in total
|
||||
#
|
||||
# By default, the message will use the humanized class name of objects
|
||||
# in collection: for instance, "project types" for ProjectType models.
|
||||
# Override this to your liking with the <tt>:entry_name</tt> parameter:
|
||||
#
|
||||
# <%= page_entries_info @posts, :entry_name => 'item' %>
|
||||
# #-> Displaying items 6 - 10 of 26 in total
|
||||
def page_entries_info(collection, options = {})
|
||||
entry_name = options[:entry_name] ||
|
||||
(collection.empty?? 'entry' : collection.first.class.name.underscore.sub('_', ' '))
|
||||
|
||||
if collection.total_pages < 2
|
||||
case collection.size
|
||||
when 0; "No #{entry_name.pluralize} found"
|
||||
when 1; "Displaying <b>1</b> #{entry_name}"
|
||||
else; "Displaying <b>all #{collection.size}</b> #{entry_name.pluralize}"
|
||||
end
|
||||
else
|
||||
%{Displaying #{entry_name.pluralize} <b>%d - %d</b> of <b>%d</b> in total} % [
|
||||
collection.offset + 1,
|
||||
collection.offset + collection.length,
|
||||
collection.total_entries
|
||||
|
|
@ -111,26 +166,63 @@ module WillPaginate
|
|||
end
|
||||
end
|
||||
|
||||
def self.total_pages_for_collection(collection) #:nodoc:
|
||||
if collection.respond_to?('page_count') and !collection.respond_to?('total_pages')
|
||||
WillPaginate::Deprecation.warn <<-MSG
|
||||
You are using a paginated collection of class #{collection.class.name}
|
||||
which conforms to the old API of WillPaginate::Collection by using
|
||||
`page_count`, while the current method name is `total_pages`. Please
|
||||
upgrade yours or 3rd-party code that provides the paginated collection.
|
||||
MSG
|
||||
class << collection
|
||||
def total_pages; page_count; end
|
||||
end
|
||||
end
|
||||
collection.total_pages
|
||||
end
|
||||
end
|
||||
|
||||
# This class does the heavy lifting of actually building the pagination
|
||||
# links. It is used by +will_paginate+ helper internally.
|
||||
class LinkRenderer
|
||||
|
||||
def initialize(collection, options, template)
|
||||
# The gap in page links is represented by:
|
||||
#
|
||||
# <span class="gap">…</span>
|
||||
attr_accessor :gap_marker
|
||||
|
||||
def initialize
|
||||
@gap_marker = '<span class="gap">…</span>'
|
||||
end
|
||||
|
||||
# * +collection+ is a WillPaginate::Collection instance or any other object
|
||||
# that conforms to that API
|
||||
# * +options+ are forwarded from +will_paginate+ view helper
|
||||
# * +template+ is the reference to the template being rendered
|
||||
def prepare(collection, options, template)
|
||||
@collection = collection
|
||||
@options = options
|
||||
@template = template
|
||||
|
||||
# reset values in case we're re-using this instance
|
||||
@total_pages = @param_name = @url_string = nil
|
||||
end
|
||||
|
||||
# Process it! This method returns the complete HTML string which contains
|
||||
# pagination links. Feel free to subclass LinkRenderer and change this
|
||||
# method as you see fit.
|
||||
def to_html
|
||||
links = @options[:page_links] ? windowed_links : []
|
||||
# previous/next buttons
|
||||
links.unshift page_link_or_span(@collection.previous_page, 'disabled', @options[:prev_label])
|
||||
links.push page_link_or_span(@collection.next_page, 'disabled', @options[:next_label])
|
||||
links.unshift page_link_or_span(@collection.previous_page, 'disabled prev_page', @options[:prev_label])
|
||||
links.push page_link_or_span(@collection.next_page, 'disabled next_page', @options[:next_label])
|
||||
|
||||
html = links.join(@options[:separator])
|
||||
@options[:container] ? @template.content_tag(:div, html, html_attributes) : html
|
||||
end
|
||||
|
||||
# Returns the subset of +options+ this instance was initialized with that
|
||||
# represent HTML attributes for the container element of pagination links.
|
||||
def html_attributes
|
||||
return @html_attributes if @html_attributes
|
||||
@html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
|
||||
|
|
@ -143,20 +235,21 @@ module WillPaginate
|
|||
|
||||
protected
|
||||
|
||||
def gap_marker; '...'; end
|
||||
|
||||
# Collects link items for visible page numbers.
|
||||
def windowed_links
|
||||
prev = nil
|
||||
|
||||
visible_page_numbers.inject [] do |links, n|
|
||||
# detect gaps:
|
||||
links << gap_marker if prev and n > prev + 1
|
||||
links << page_link_or_span(n)
|
||||
links << page_link_or_span(n, 'current')
|
||||
prev = n
|
||||
links
|
||||
end
|
||||
end
|
||||
|
||||
# Calculates visible page numbers using the <tt>:inner_window</tt> and
|
||||
# <tt>:outer_window</tt> options.
|
||||
def visible_page_numbers
|
||||
inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
|
||||
window_from = current_page - inner_window
|
||||
|
|
@ -166,9 +259,11 @@ module WillPaginate
|
|||
if window_to > total_pages
|
||||
window_from -= window_to - total_pages
|
||||
window_to = total_pages
|
||||
elsif window_from < 1
|
||||
end
|
||||
if window_from < 1
|
||||
window_to += 1 - window_from
|
||||
window_from = 1
|
||||
window_to = total_pages if window_to > total_pages
|
||||
end
|
||||
|
||||
visible = (1..total_pages).to_a
|
||||
|
|
@ -180,21 +275,63 @@ module WillPaginate
|
|||
visible
|
||||
end
|
||||
|
||||
def page_link_or_span(page, span_class = 'current', text = nil)
|
||||
def page_link_or_span(page, span_class, text = nil)
|
||||
text ||= page.to_s
|
||||
|
||||
if page and page != current_page
|
||||
@template.link_to text, url_options(page), :rel => rel_value(page)
|
||||
classnames = span_class && span_class.index(' ') && span_class.split(' ', 2).last
|
||||
page_link page, text, :rel => rel_value(page), :class => classnames
|
||||
else
|
||||
@template.content_tag :span, text, :class => span_class
|
||||
page_span page, text, :class => span_class
|
||||
end
|
||||
end
|
||||
|
||||
def url_options(page)
|
||||
options = { param_name => page }
|
||||
def page_link(page, text, attributes = {})
|
||||
@template.link_to text, url_for(page), attributes
|
||||
end
|
||||
|
||||
def page_span(page, text, attributes = {})
|
||||
@template.content_tag :span, text, attributes
|
||||
end
|
||||
|
||||
# Returns URL params for +page_link_or_span+, taking the current GET params
|
||||
# and <tt>:params</tt> option into account.
|
||||
def url_for(page)
|
||||
page_one = page == 1
|
||||
unless @url_string and !page_one
|
||||
@url_params = {}
|
||||
# page links should preserve GET parameters
|
||||
options = params.merge(options) if @template.request.get?
|
||||
options.rec_merge!(@options[:params]) if @options[:params]
|
||||
return options
|
||||
stringified_merge @url_params, @template.params if @template.request.get?
|
||||
stringified_merge @url_params, @options[:params] if @options[:params]
|
||||
|
||||
if complex = param_name.index(/[^\w-]/)
|
||||
page_param = (defined?(CGIMethods) ? CGIMethods : ActionController::AbstractRequest).
|
||||
parse_query_parameters("#{param_name}=#{page}")
|
||||
|
||||
stringified_merge @url_params, page_param
|
||||
else
|
||||
@url_params[param_name] = page_one ? 1 : 2
|
||||
end
|
||||
|
||||
url = @template.url_for(@url_params)
|
||||
return url if page_one
|
||||
|
||||
if complex
|
||||
@url_string = url.sub(%r!((?:\?|&)#{CGI.escape param_name}=)#{page}!, '\1@')
|
||||
return url
|
||||
else
|
||||
@url_string = url
|
||||
@url_params[param_name] = 3
|
||||
@template.url_for(@url_params).split(//).each_with_index do |char, i|
|
||||
if char == '3' and url[i, 1] == '2'
|
||||
@url_string[i] = '@'
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# finally!
|
||||
@url_string.sub '@', page.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -212,15 +349,25 @@ module WillPaginate
|
|||
end
|
||||
|
||||
def total_pages
|
||||
@collection.page_count
|
||||
@total_pages ||= WillPaginate::ViewHelpers.total_pages_for_collection(@collection)
|
||||
end
|
||||
|
||||
def param_name
|
||||
@param_name ||= @options[:param_name].to_sym
|
||||
@param_name ||= @options[:param_name].to_s
|
||||
end
|
||||
|
||||
def params
|
||||
@params ||= @template.params.to_hash.symbolize_keys
|
||||
# Recursively merge into target hash by using stringified keys from the other one
|
||||
def stringified_merge(target, other)
|
||||
other.each do |key, value|
|
||||
key = key.to_s # this line is what it's all about!
|
||||
existing = target[key]
|
||||
|
||||
if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?)
|
||||
stringified_merge(existing || (target[key] = {}), value)
|
||||
else
|
||||
target[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
2
vendor/plugins/will_paginate/test/boot.rb
vendored
2
vendor/plugins/will_paginate/test/boot.rb
vendored
|
|
@ -19,5 +19,3 @@ else
|
|||
gem 'activerecord'
|
||||
end
|
||||
end
|
||||
|
||||
$:.unshift "#{plugin_root}/lib"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
require File.dirname(__FILE__) + '/helper'
|
||||
require 'will_paginate/core_ext'
|
||||
require 'helper'
|
||||
require 'will_paginate/array'
|
||||
|
||||
class ArrayPaginationTest < Test::Unit::TestCase
|
||||
def test_simple
|
||||
|
|
@ -11,7 +11,8 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
|||
{ :page => 3, :per_page => 5, :expected => [] },
|
||||
].
|
||||
each do |conditions|
|
||||
assert_equal conditions[:expected], collection.paginate(conditions.slice(:page, :per_page))
|
||||
expected = conditions.delete :expected
|
||||
assert_equal expected, collection.paginate(conditions)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -22,14 +23,8 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_deprecated_api
|
||||
assert_deprecated 'paginate API' do
|
||||
result = (1..50).to_a.paginate(2, 10)
|
||||
assert_equal 2, result.current_page
|
||||
assert_equal (11..20).to_a, result
|
||||
assert_equal 50, result.total_entries
|
||||
end
|
||||
|
||||
assert_deprecated { [].paginate nil }
|
||||
assert_raise(ArgumentError) { [].paginate(2) }
|
||||
assert_raise(ArgumentError) { [].paginate(2, 10) }
|
||||
end
|
||||
|
||||
def test_total_entries_has_precedence
|
||||
|
|
@ -50,14 +45,28 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
assert_equal entries, collection
|
||||
assert_respond_to_all collection, %w(page_count each offset size current_page per_page total_entries)
|
||||
assert_respond_to_all collection, %w(total_pages each offset size current_page per_page total_entries)
|
||||
assert_kind_of Array, collection
|
||||
assert_instance_of Array, collection.entries
|
||||
assert_equal 3, collection.offset
|
||||
assert_equal 4, collection.page_count
|
||||
assert_equal 4, collection.total_pages
|
||||
assert !collection.out_of_bounds?
|
||||
end
|
||||
|
||||
def test_previous_next_pages
|
||||
collection = create(1, 1, 3)
|
||||
assert_nil collection.previous_page
|
||||
assert_equal 2, collection.next_page
|
||||
|
||||
collection = create(2, 1, 3)
|
||||
assert_equal 1, collection.previous_page
|
||||
assert_equal 3, collection.next_page
|
||||
|
||||
collection = create(3, 1, 3)
|
||||
assert_equal 2, collection.previous_page
|
||||
assert_nil collection.next_page
|
||||
end
|
||||
|
||||
def test_out_of_bounds
|
||||
entries = create(2, 3, 2){}
|
||||
assert entries.out_of_bounds?
|
||||
|
|
@ -90,13 +99,20 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
|||
pager.replace array(0)
|
||||
end
|
||||
assert_equal nil, entries.total_entries
|
||||
|
||||
entries = create(1) do |pager|
|
||||
# collection is empty and we're on page 1,
|
||||
# so the whole thing must be empty, too
|
||||
pager.replace array(0)
|
||||
end
|
||||
assert_equal 0, entries.total_entries
|
||||
end
|
||||
|
||||
def test_invalid_page
|
||||
bad_input = [0, -1, nil, '', 'Schnitzel']
|
||||
bad_inputs = [0, -1, nil, '', 'Schnitzel']
|
||||
|
||||
bad_input.each do |bad|
|
||||
assert_raise(WillPaginate::InvalidPage) { create(bad) }
|
||||
bad_inputs.each do |bad|
|
||||
assert_raise(WillPaginate::InvalidPage) { create bad }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -104,6 +120,11 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
|||
assert_raise(ArgumentError) { create(1, -1) }
|
||||
end
|
||||
|
||||
def test_page_count_was_removed
|
||||
assert_raise(NoMethodError) { create.page_count }
|
||||
# It's `total_pages` now.
|
||||
end
|
||||
|
||||
private
|
||||
def create(page = 2, limit = 5, total = nil, &block)
|
||||
if block_given?
|
||||
|
|
@ -116,16 +137,4 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
|||
def array(size = 3)
|
||||
Array.new(size)
|
||||
end
|
||||
|
||||
def collect_deprecations
|
||||
old_behavior = WillPaginate::Deprecation.behavior
|
||||
deprecations = []
|
||||
WillPaginate::Deprecation.behavior = Proc.new do |message, callstack|
|
||||
deprecations << message
|
||||
end
|
||||
result = yield
|
||||
[result, deprecations]
|
||||
ensure
|
||||
WillPaginate::Deprecation.behavior = old_behavior
|
||||
end
|
||||
end
|
||||
5
vendor/plugins/will_paginate/test/console
vendored
5
vendor/plugins/will_paginate/test/console
vendored
|
|
@ -1,9 +1,8 @@
|
|||
#!/usr/bin/env ruby
|
||||
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
||||
libs = []
|
||||
dirname = File.dirname(__FILE__)
|
||||
|
||||
libs << 'irb/completion'
|
||||
libs << File.join(dirname, 'lib', 'load_fixtures')
|
||||
libs << File.join('lib', 'load_fixtures')
|
||||
|
||||
exec "#{irb}#{libs.map{ |l| " -r #{l}" }.join} --simple-prompt"
|
||||
exec "#{irb} -Ilib:test#{libs.map{ |l| " -r #{l}" }.join} --simple-prompt"
|
||||
|
|
|
|||
158
vendor/plugins/will_paginate/test/finder_test.rb
vendored
158
vendor/plugins/will_paginate/test/finder_test.rb
vendored
|
|
@ -1,8 +1,9 @@
|
|||
require File.dirname(__FILE__) + '/helper'
|
||||
require File.dirname(__FILE__) + '/lib/activerecord_test_case'
|
||||
require 'helper'
|
||||
require 'lib/activerecord_test_case'
|
||||
|
||||
require 'will_paginate'
|
||||
WillPaginate.enable_activerecord
|
||||
WillPaginate.enable_named_scope
|
||||
|
||||
class FinderTest < ActiveRecordTestCase
|
||||
fixtures :topics, :replies, :users, :projects, :developers_projects
|
||||
|
|
@ -12,19 +13,19 @@ class FinderTest < ActiveRecordTestCase
|
|||
end
|
||||
|
||||
def test_simple_paginate
|
||||
assert_queries(1) do
|
||||
entries = Topic.paginate :page => nil
|
||||
assert_equal 1, entries.current_page
|
||||
assert_nil entries.previous_page
|
||||
assert_nil entries.next_page
|
||||
assert_equal 1, entries.page_count
|
||||
assert_equal 1, entries.total_pages
|
||||
assert_equal 4, entries.size
|
||||
end
|
||||
|
||||
assert_queries(2) do
|
||||
entries = Topic.paginate :page => 2
|
||||
assert_equal 2, entries.current_page
|
||||
assert_equal 1, entries.previous_page
|
||||
assert_equal 1, entries.page_count
|
||||
assert_equal 1, entries.total_pages
|
||||
assert entries.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def test_parameter_api
|
||||
# :page parameter in options is required!
|
||||
|
|
@ -41,31 +42,31 @@ class FinderTest < ActiveRecordTestCase
|
|||
def test_paginate_with_per_page
|
||||
entries = Topic.paginate :page => 1, :per_page => 1
|
||||
assert_equal 1, entries.size
|
||||
assert_equal 4, entries.page_count
|
||||
assert_equal 4, entries.total_pages
|
||||
|
||||
# Developer class has explicit per_page at 10
|
||||
entries = Developer.paginate :page => 1
|
||||
assert_equal 10, entries.size
|
||||
assert_equal 2, entries.page_count
|
||||
assert_equal 2, entries.total_pages
|
||||
|
||||
entries = Developer.paginate :page => 1, :per_page => 5
|
||||
assert_equal 11, entries.total_entries
|
||||
assert_equal 5, entries.size
|
||||
assert_equal 3, entries.page_count
|
||||
assert_equal 3, entries.total_pages
|
||||
end
|
||||
|
||||
def test_paginate_with_order
|
||||
entries = Topic.paginate :page => 1, :order => 'created_at desc'
|
||||
expected = [topics(:futurama), topics(:harvey_birdman), topics(:rails), topics(:ar)].reverse
|
||||
assert_equal expected, entries.to_a
|
||||
assert_equal 1, entries.page_count
|
||||
assert_equal 1, entries.total_pages
|
||||
end
|
||||
|
||||
def test_paginate_with_conditions
|
||||
entries = Topic.paginate :page => 1, :conditions => ["created_at > ?", 30.minutes.ago]
|
||||
expected = [topics(:rails), topics(:ar)]
|
||||
assert_equal expected, entries.to_a
|
||||
assert_equal 1, entries.page_count
|
||||
assert_equal 1, entries.total_pages
|
||||
end
|
||||
|
||||
def test_paginate_with_include_and_conditions
|
||||
|
|
@ -85,11 +86,14 @@ class FinderTest < ActiveRecordTestCase
|
|||
end
|
||||
|
||||
def test_paginate_with_include_and_order
|
||||
entries = nil
|
||||
assert_queries(2) do
|
||||
entries = Topic.paginate \
|
||||
:page => 1,
|
||||
:include => :replies,
|
||||
:order => 'replies.created_at asc, topics.created_at asc',
|
||||
:per_page => 10
|
||||
end
|
||||
|
||||
expected = Topic.find :all,
|
||||
:include => 'replies',
|
||||
|
|
@ -104,7 +108,7 @@ class FinderTest < ActiveRecordTestCase
|
|||
entries, project = nil, projects(:active_record)
|
||||
|
||||
assert_nothing_raised "THIS IS A BUG in Rails 1.2.3 that was fixed in [7326]. " +
|
||||
"Please upgrade to the 1-2-stable branch or edge Rails." do
|
||||
"Please upgrade to a newer version of Rails." do
|
||||
entries = project.topics.paginate \
|
||||
:page => 1,
|
||||
:include => :replies,
|
||||
|
|
@ -125,10 +129,12 @@ class FinderTest < ActiveRecordTestCase
|
|||
expected_name_ordered = [projects(:action_controller), projects(:active_record)]
|
||||
expected_id_ordered = [projects(:active_record), projects(:action_controller)]
|
||||
|
||||
assert_queries(2) do
|
||||
# with association-specified order
|
||||
entries = dhh.projects.paginate(:page => 1)
|
||||
assert_equal expected_name_ordered, entries
|
||||
assert_equal 2, entries.total_entries
|
||||
end
|
||||
|
||||
# with explicit order
|
||||
entries = dhh.projects.paginate(:page => 1, :order => 'projects.id')
|
||||
|
|
@ -148,29 +154,43 @@ class FinderTest < ActiveRecordTestCase
|
|||
|
||||
def test_paginate_association_extension
|
||||
project = Project.find(:first)
|
||||
|
||||
assert_queries(2) do
|
||||
entries = project.replies.paginate_recent :page => 1
|
||||
assert_equal [replies(:brave)], entries
|
||||
end
|
||||
end
|
||||
|
||||
def test_paginate_with_joins
|
||||
entries = nil
|
||||
|
||||
assert_queries(1) do
|
||||
entries = Developer.paginate :page => 1,
|
||||
:joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
|
||||
:conditions => 'project_id = 1'
|
||||
assert_equal 2, entries.size
|
||||
developer_names = entries.map { |d| d.name }
|
||||
developer_names = entries.map &:name
|
||||
assert developer_names.include?('David')
|
||||
assert developer_names.include?('Jamis')
|
||||
end
|
||||
|
||||
assert_queries(1) do
|
||||
expected = entries.to_a
|
||||
entries = Developer.paginate :page => 1,
|
||||
:joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
|
||||
:conditions => 'project_id = 1', :count => { :select => "users.id" }
|
||||
assert_equal expected, entries.to_a
|
||||
assert_equal 2, entries.total_entries
|
||||
end
|
||||
end
|
||||
|
||||
def test_paginate_with_group
|
||||
entries = nil
|
||||
assert_queries(1) do
|
||||
entries = Developer.paginate :page => 1, :per_page => 10,
|
||||
:group => 'salary', :select => 'salary', :order => 'salary'
|
||||
end
|
||||
|
||||
expected = [ users(:david), users(:jamis), users(:dev_10), users(:poor_jamis) ].map(&:salary).sort
|
||||
assert_equal expected, entries.map(&:salary)
|
||||
end
|
||||
|
|
@ -201,6 +221,56 @@ class FinderTest < ActiveRecordTestCase
|
|||
assert_equal 2, entries.total_entries
|
||||
end
|
||||
|
||||
## named_scope ##
|
||||
|
||||
def test_paginate_in_named_scope
|
||||
entries = Developer.poor.paginate :page => 1, :per_page => 1
|
||||
|
||||
assert_equal 1, entries.size
|
||||
assert_equal 2, entries.total_entries
|
||||
end
|
||||
|
||||
def test_paginate_in_named_scope_on_habtm_association
|
||||
project = projects(:active_record)
|
||||
assert_queries(2) do
|
||||
entries = project.developers.poor.paginate :page => 1, :per_page => 1
|
||||
|
||||
assert_equal 1, entries.size, 'one developer should be found'
|
||||
assert_equal 1, entries.total_entries, 'only one developer should be found'
|
||||
end
|
||||
end
|
||||
|
||||
def test_paginate_in_named_scope_on_hmt_association
|
||||
project = projects(:active_record)
|
||||
expected = [replies(:brave)]
|
||||
|
||||
assert_queries(2) do
|
||||
entries = project.replies.recent.paginate :page => 1, :per_page => 1
|
||||
assert_equal expected, entries
|
||||
assert_equal 1, entries.total_entries, 'only one reply should be found'
|
||||
end
|
||||
end
|
||||
|
||||
def test_paginate_in_named_scope_on_has_many_association
|
||||
project = projects(:active_record)
|
||||
expected = [topics(:ar)]
|
||||
|
||||
assert_queries(2) do
|
||||
entries = project.topics.mentions_activerecord.paginate :page => 1, :per_page => 1
|
||||
assert_equal expected, entries
|
||||
assert_equal 1, entries.total_entries, 'only one topic should be found'
|
||||
end
|
||||
end
|
||||
|
||||
## misc ##
|
||||
|
||||
def test_count_and_total_entries_options_are_mutually_exclusive
|
||||
e = assert_raise ArgumentError do
|
||||
Developer.paginate :page => 1, :count => {}, :total_entries => 1
|
||||
end
|
||||
assert_match /exclusive/, e.to_s
|
||||
end
|
||||
|
||||
def test_readonly
|
||||
assert_nothing_raised { Developer.paginate :readonly => true, :page => 1 }
|
||||
end
|
||||
|
|
@ -216,21 +286,23 @@ class FinderTest < ActiveRecordTestCase
|
|||
end
|
||||
|
||||
# Is this Rails 2.0? Find out by testing find_all which was removed in [6998]
|
||||
unless Developer.respond_to? :find_all
|
||||
unless ActiveRecord::Base.respond_to? :find_all
|
||||
def test_paginate_array_of_ids
|
||||
# AR finders also accept arrays of IDs
|
||||
# (this was broken in Rails before [6912])
|
||||
assert_queries(1) do
|
||||
entries = Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id')
|
||||
assert_equal (4..6).to_a, entries.map(&:id)
|
||||
assert_equal 8, entries.total_entries
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
uses_mocha 'internals' do
|
||||
def test_implicit_all_with_dynamic_finders
|
||||
Topic.expects(:find_all_by_foo).returns([])
|
||||
Topic.expects(:count).returns(0)
|
||||
Topic.paginate_by_foo :page => 1
|
||||
Topic.paginate_by_foo :page => 2
|
||||
end
|
||||
|
||||
def test_guessing_the_total_count
|
||||
|
|
@ -241,6 +313,14 @@ class FinderTest < ActiveRecordTestCase
|
|||
assert_equal 6, entries.total_entries
|
||||
end
|
||||
|
||||
def test_guessing_that_there_are_no_records
|
||||
Topic.expects(:find).returns([])
|
||||
Topic.expects(:count).never
|
||||
|
||||
entries = Topic.paginate :page => 1, :per_page => 4
|
||||
assert_equal 0, entries.total_entries
|
||||
end
|
||||
|
||||
def test_extra_parameters_stay_untouched
|
||||
Topic.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5))
|
||||
Topic.expects(:count).with({:foo => 'bar'}).returns(1)
|
||||
|
|
@ -251,13 +331,13 @@ class FinderTest < ActiveRecordTestCase
|
|||
def test_count_skips_select
|
||||
Developer.stubs(:find).returns([])
|
||||
Developer.expects(:count).with({}).returns(0)
|
||||
Developer.paginate :select => 'salary', :page => 1
|
||||
Developer.paginate :select => 'salary', :page => 2
|
||||
end
|
||||
|
||||
def test_count_select_when_distinct
|
||||
Developer.stubs(:find).returns([])
|
||||
Developer.expects(:count).with(:select => 'DISTINCT salary').returns(0)
|
||||
Developer.paginate :select => 'DISTINCT salary', :page => 1
|
||||
Developer.paginate :select => 'DISTINCT salary', :page => 2
|
||||
end
|
||||
|
||||
def test_should_use_scoped_finders_if_present
|
||||
|
|
@ -288,16 +368,16 @@ class FinderTest < ActiveRecordTestCase
|
|||
Developer.expects(:find_by_sql).returns([])
|
||||
Developer.expects(:count_by_sql).with("SELECT COUNT(*) FROM (sql\n ) AS count_table").returns(0)
|
||||
|
||||
entries = Developer.paginate_by_sql "sql\n ORDER\nby foo, bar, `baz` ASC", :page => 1
|
||||
Developer.paginate_by_sql "sql\n ORDER\nby foo, bar, `baz` ASC", :page => 2
|
||||
end
|
||||
|
||||
# TODO: counts are still wrong
|
||||
def test_ability_to_use_with_custom_finders
|
||||
# acts_as_taggable defines find_tagged_with(tag, options)
|
||||
Topic.expects(:find_tagged_with).with('will_paginate', :offset => 0, :limit => 5).returns([])
|
||||
Topic.expects(:find_tagged_with).with('will_paginate', :offset => 5, :limit => 5).returns([])
|
||||
Topic.expects(:count).with({}).returns(0)
|
||||
|
||||
Topic.paginate_tagged_with 'will_paginate', :page => 1, :per_page => 5
|
||||
Topic.paginate_tagged_with 'will_paginate', :page => 2, :per_page => 5
|
||||
end
|
||||
|
||||
def test_array_argument_doesnt_eliminate_count
|
||||
|
|
@ -310,7 +390,6 @@ class FinderTest < ActiveRecordTestCase
|
|||
|
||||
def test_paginating_finder_doesnt_mangle_options
|
||||
Developer.expects(:find).returns([])
|
||||
Developer.expects(:count).returns(0)
|
||||
options = { :page => 1 }
|
||||
options.expects(:delete).never
|
||||
options_before = options.dup
|
||||
|
|
@ -318,5 +397,38 @@ class FinderTest < ActiveRecordTestCase
|
|||
Developer.paginate(options)
|
||||
assert_equal options, options_before
|
||||
end
|
||||
|
||||
def test_paginated_each
|
||||
collection = stub('collection', :size => 5, :empty? => false, :per_page => 5)
|
||||
collection.expects(:each).times(2).returns(collection)
|
||||
last_collection = stub('collection', :size => 4, :empty? => false, :per_page => 5)
|
||||
last_collection.expects(:each).returns(last_collection)
|
||||
|
||||
params = { :order => 'id', :total_entries => 0 }
|
||||
|
||||
Developer.expects(:paginate).with(params.merge(:page => 2)).returns(collection)
|
||||
Developer.expects(:paginate).with(params.merge(:page => 3)).returns(collection)
|
||||
Developer.expects(:paginate).with(params.merge(:page => 4)).returns(last_collection)
|
||||
|
||||
assert_equal 14, Developer.paginated_each(:page => '2') { }
|
||||
end
|
||||
|
||||
# detect ActiveRecord 2.1
|
||||
if ActiveRecord::Base.private_methods.include?('references_eager_loaded_tables?')
|
||||
def test_removes_irrelevant_includes_in_count
|
||||
Developer.expects(:find).returns([1])
|
||||
Developer.expects(:count).with({}).returns(0)
|
||||
|
||||
Developer.paginate :page => 1, :per_page => 1, :include => :projects
|
||||
end
|
||||
|
||||
def test_doesnt_remove_referenced_includes_in_count
|
||||
Developer.expects(:find).returns([1])
|
||||
Developer.expects(:count).with({ :include => :projects, :conditions => 'projects.id > 2' }).returns(0)
|
||||
|
||||
Developer.paginate :page => 1, :per_page => 1,
|
||||
:include => :projects, :conditions => 'projects.id > 2'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,5 +7,7 @@ class Developer < User
|
|||
end
|
||||
end
|
||||
|
||||
named_scope :poor, :conditions => ['salary <= ?', 80000], :order => 'salary'
|
||||
|
||||
def self.per_page() 10 end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
action_controller:
|
||||
id: 2
|
||||
name: Active Controller
|
||||
|
||||
active_record:
|
||||
id: 1
|
||||
name: Active Record
|
||||
action_controller:
|
||||
id: 2
|
||||
name: Active Controller
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
class Reply < ActiveRecord::Base
|
||||
belongs_to :topic, :include => [:replies]
|
||||
|
||||
named_scope :recent, :conditions => ['replies.created_at > ?', 15.minutes.ago]
|
||||
|
||||
validates_presence_of :content
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
class Topic < ActiveRecord::Base
|
||||
has_many :replies, :dependent => :destroy, :order => 'replies.created_at DESC'
|
||||
belongs_to :project
|
||||
|
||||
named_scope :mentions_activerecord, :conditions => ['topics.title LIKE ?', '%ActiveRecord%']
|
||||
end
|
||||
|
|
|
|||
14
vendor/plugins/will_paginate/test/helper.rb
vendored
14
vendor/plugins/will_paginate/test/helper.rb
vendored
|
|
@ -4,7 +4,7 @@ require 'rubygems'
|
|||
# gem install redgreen for colored test output
|
||||
begin require 'redgreen'; rescue LoadError; end
|
||||
|
||||
require File.join(File.dirname(__FILE__), 'boot') unless defined?(ActiveRecord)
|
||||
require 'boot' unless defined?(ActiveRecord)
|
||||
|
||||
class Test::Unit::TestCase
|
||||
protected
|
||||
|
|
@ -13,6 +13,18 @@ class Test::Unit::TestCase
|
|||
[method.to_s, method.to_sym].each { |m| assert_respond_to object, m }
|
||||
end
|
||||
end
|
||||
|
||||
def collect_deprecations
|
||||
old_behavior = WillPaginate::Deprecation.behavior
|
||||
deprecations = []
|
||||
WillPaginate::Deprecation.behavior = Proc.new do |message, callstack|
|
||||
deprecations << message
|
||||
end
|
||||
result = yield
|
||||
[result, deprecations]
|
||||
ensure
|
||||
WillPaginate::Deprecation.behavior = old_behavior
|
||||
end
|
||||
end
|
||||
|
||||
# Wrap tests that use Mocha and skip if unavailable.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
require File.join(File.dirname(__FILE__), 'activerecord_test_connector')
|
||||
require 'lib/activerecord_test_connector'
|
||||
|
||||
class ActiveRecordTestCase < Test::Unit::TestCase
|
||||
# Set our fixture path
|
||||
|
|
@ -18,6 +18,19 @@ class ActiveRecordTestCase < Test::Unit::TestCase
|
|||
# Default so Test::Unit::TestCase doesn't complain
|
||||
def test_truth
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def assert_queries(num = 1)
|
||||
$query_count = 0
|
||||
yield
|
||||
ensure
|
||||
assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
|
||||
end
|
||||
|
||||
def assert_no_queries(&block)
|
||||
assert_queries(0, &block)
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecordTestConnector.setup
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ class ActiveRecordTestConnector
|
|||
cattr_accessor :able_to_connect
|
||||
cattr_accessor :connected
|
||||
|
||||
FIXTURES_PATH = File.join(File.dirname(__FILE__), '..', 'fixtures')
|
||||
|
||||
# Set our defaults
|
||||
self.connected = false
|
||||
self.able_to_connect = true
|
||||
|
|
@ -14,13 +16,12 @@ class ActiveRecordTestConnector
|
|||
unless self.connected || !self.able_to_connect
|
||||
setup_connection
|
||||
load_schema
|
||||
# require_fixture_models
|
||||
Dependencies.load_paths.unshift(File.dirname(__FILE__) + "/../fixtures")
|
||||
Dependencies.load_paths.unshift FIXTURES_PATH
|
||||
self.connected = true
|
||||
end
|
||||
rescue Exception => e # errors from ActiveRecord setup
|
||||
$stderr.puts "\nSkipping ActiveRecord assertion tests: #{e}"
|
||||
#$stderr.puts " #{e.backtrace.join("\n ")}\n"
|
||||
$stderr.puts "\nSkipping ActiveRecord tests: #{e}"
|
||||
$stderr.puts "Install SQLite3 to run the full test suite for will_paginate.\n\n"
|
||||
self.able_to_connect = false
|
||||
end
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ class ActiveRecordTestConnector
|
|||
|
||||
ActiveRecord::Base.establish_connection(configuration)
|
||||
ActiveRecord::Base.configurations = { db => configuration }
|
||||
ActiveRecord::Base.connection
|
||||
prepare ActiveRecord::Base.connection
|
||||
|
||||
unless Object.const_defined?(:QUOTED_TYPE)
|
||||
Object.send :const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')
|
||||
|
|
@ -48,13 +49,21 @@ class ActiveRecordTestConnector
|
|||
def self.load_schema
|
||||
ActiveRecord::Base.silence do
|
||||
ActiveRecord::Migration.verbose = false
|
||||
load File.dirname(__FILE__) + "/../fixtures/schema.rb"
|
||||
load File.join(FIXTURES_PATH, 'schema.rb')
|
||||
end
|
||||
end
|
||||
|
||||
def self.require_fixture_models
|
||||
models = Dir.glob(File.dirname(__FILE__) + "/../fixtures/*.rb")
|
||||
models = (models.grep(/user.rb/) + models).uniq
|
||||
models.each { |f| require f }
|
||||
def self.prepare(conn)
|
||||
class << conn
|
||||
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
|
||||
|
||||
def execute_with_counting(sql, name = nil, &block)
|
||||
$query_count ||= 0
|
||||
$query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
|
||||
execute_without_counting(sql, name, &block)
|
||||
end
|
||||
|
||||
alias_method_chain :execute, :counting
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
require 'action_controller/test_process'
|
||||
|
||||
module HTML
|
||||
class Node
|
||||
def inner_text
|
||||
children.map(&:inner_text).join('')
|
||||
end
|
||||
end
|
||||
|
||||
class Text
|
||||
def inner_text
|
||||
self.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class Tag
|
||||
def inner_text
|
||||
childless?? '' : super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
dirname = File.dirname(__FILE__)
|
||||
require File.join(dirname, '..', 'boot')
|
||||
require File.join(dirname, 'activerecord_test_connector')
|
||||
require 'boot'
|
||||
require 'lib/activerecord_test_connector'
|
||||
|
||||
# setup the connection
|
||||
ActiveRecordTestConnector.setup
|
||||
|
||||
# load all fixtures
|
||||
fixture_path = File.join(dirname, '..', 'fixtures')
|
||||
Fixtures.create_fixtures(fixture_path, ActiveRecord::Base.connection.tables)
|
||||
Fixtures.create_fixtures(ActiveRecordTestConnector::FIXTURES_PATH, ActiveRecord::Base.connection.tables)
|
||||
|
||||
require 'will_paginate'
|
||||
WillPaginate.enable_activerecord
|
||||
|
|
|
|||
165
vendor/plugins/will_paginate/test/lib/view_test_process.rb
vendored
Normal file
165
vendor/plugins/will_paginate/test/lib/view_test_process.rb
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
require 'action_controller'
|
||||
require 'action_controller/test_process'
|
||||
|
||||
require 'will_paginate'
|
||||
WillPaginate.enable_actionpack
|
||||
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.connect 'dummy/page/:page', :controller => 'dummy'
|
||||
map.connect 'dummy/dots/page.:page', :controller => 'dummy', :action => 'dots'
|
||||
map.connect 'ibocorp/:page', :controller => 'ibocorp',
|
||||
:requirements => { :page => /\d+/ },
|
||||
:defaults => { :page => 1 }
|
||||
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
|
||||
ActionController::Base.perform_caching = false
|
||||
|
||||
class WillPaginate::ViewTestCase < Test::Unit::TestCase
|
||||
def setup
|
||||
super
|
||||
@controller = DummyController.new
|
||||
@request = @controller.request
|
||||
@html_result = nil
|
||||
@template = '<%= will_paginate collection, options %>'
|
||||
|
||||
@view = ActionView::Base.new
|
||||
@view.assigns['controller'] = @controller
|
||||
@view.assigns['_request'] = @request
|
||||
@view.assigns['_params'] = @request.params
|
||||
end
|
||||
|
||||
def test_no_complain; end
|
||||
|
||||
protected
|
||||
|
||||
def paginate(collection = {}, options = {}, &block)
|
||||
if collection.instance_of? Hash
|
||||
page_options = { :page => 1, :total_entries => 11, :per_page => 4 }.merge(collection)
|
||||
collection = [1].paginate(page_options)
|
||||
end
|
||||
|
||||
locals = { :collection => collection, :options => options }
|
||||
|
||||
if defined? ActionView::InlineTemplate
|
||||
# Rails 2.1
|
||||
args = [ ActionView::InlineTemplate.new(@view, @template, locals) ]
|
||||
else
|
||||
# older Rails versions
|
||||
args = [nil, @template, nil, locals]
|
||||
end
|
||||
|
||||
@html_result = @view.render_template(*args)
|
||||
@html_document = HTML::Document.new(@html_result, true, false)
|
||||
|
||||
if block_given?
|
||||
classname = options[:class] || WillPaginate::ViewHelpers.pagination_options[:class]
|
||||
assert_select("div.#{classname}", 1, 'no main DIV', &block)
|
||||
end
|
||||
end
|
||||
|
||||
def response_from_page_or_rjs
|
||||
@html_document.root
|
||||
end
|
||||
|
||||
def validate_page_numbers expected, links, param_name = :page
|
||||
param_pattern = /\W#{CGI.escape(param_name.to_s)}=([^&]*)/
|
||||
|
||||
assert_equal(expected, links.map { |e|
|
||||
e['href'] =~ param_pattern
|
||||
$1 ? $1.to_i : $1
|
||||
})
|
||||
end
|
||||
|
||||
def assert_links_match pattern, links = nil, numbers = nil
|
||||
links ||= assert_select 'div.pagination a[href]' do |elements|
|
||||
elements
|
||||
end
|
||||
|
||||
pages = [] if numbers
|
||||
|
||||
links.each do |el|
|
||||
assert_match pattern, el['href']
|
||||
if numbers
|
||||
el['href'] =~ pattern
|
||||
pages << ($1.nil?? nil : $1.to_i)
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal numbers, pages, "page numbers don't match" if numbers
|
||||
end
|
||||
|
||||
def assert_no_links_match pattern
|
||||
assert_select 'div.pagination a[href]' do |elements|
|
||||
elements.each do |el|
|
||||
assert_no_match pattern, el['href']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DummyRequest
|
||||
attr_accessor :symbolized_path_parameters
|
||||
|
||||
def initialize
|
||||
@get = true
|
||||
@params = {}
|
||||
@symbolized_path_parameters = { :controller => 'foo', :action => 'bar' }
|
||||
end
|
||||
|
||||
def get?
|
||||
@get
|
||||
end
|
||||
|
||||
def post
|
||||
@get = false
|
||||
end
|
||||
|
||||
def relative_url_root
|
||||
''
|
||||
end
|
||||
|
||||
def params(more = nil)
|
||||
@params.update(more) if more
|
||||
@params
|
||||
end
|
||||
end
|
||||
|
||||
class DummyController
|
||||
attr_reader :request
|
||||
attr_accessor :controller_name
|
||||
|
||||
def initialize
|
||||
@request = DummyRequest.new
|
||||
@url = ActionController::UrlRewriter.new(@request, @request.params)
|
||||
end
|
||||
|
||||
def params
|
||||
@request.params
|
||||
end
|
||||
|
||||
def url_for(params)
|
||||
@url.rewrite(params)
|
||||
end
|
||||
end
|
||||
|
||||
module HTML
|
||||
Node.class_eval do
|
||||
def inner_text
|
||||
children.map(&:inner_text).join('')
|
||||
end
|
||||
end
|
||||
|
||||
Text.class_eval do
|
||||
def inner_text
|
||||
self.to_s
|
||||
end
|
||||
end
|
||||
|
||||
Tag.class_eval do
|
||||
def inner_text
|
||||
childless?? '' : super
|
||||
end
|
||||
end
|
||||
end
|
||||
272
vendor/plugins/will_paginate/test/pagination_test.rb
vendored
272
vendor/plugins/will_paginate/test/pagination_test.rb
vendored
|
|
@ -1,272 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/helper'
|
||||
require 'action_controller'
|
||||
require File.dirname(__FILE__) + '/lib/html_inner_text'
|
||||
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
|
||||
ActionController::Base.perform_caching = false
|
||||
|
||||
require 'will_paginate'
|
||||
WillPaginate.enable_actionpack
|
||||
|
||||
class PaginationTest < Test::Unit::TestCase
|
||||
|
||||
class DevelopersController < ActionController::Base
|
||||
def list_developers
|
||||
@options = session[:wp] || {}
|
||||
|
||||
@developers = (1..11).to_a.paginate(
|
||||
:page => params[@options[:param_name] || :page] || 1,
|
||||
:per_page => params[:per_page] || 4
|
||||
)
|
||||
|
||||
render :inline => '<%= will_paginate @developers, @options %>'
|
||||
end
|
||||
|
||||
def guess_collection_name
|
||||
@developers = session[:wp]
|
||||
@options = session[:wp_options]
|
||||
render :inline => '<%= will_paginate @options %>'
|
||||
end
|
||||
|
||||
protected
|
||||
def rescue_errors(e) raise e end
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
def setup
|
||||
@controller = DevelopersController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
super
|
||||
end
|
||||
|
||||
def test_will_paginate
|
||||
get :list_developers
|
||||
|
||||
entries = assigns :developers
|
||||
assert entries
|
||||
assert_equal 4, entries.size
|
||||
|
||||
assert_select 'div.pagination', 1, 'no main DIV' do |pagination|
|
||||
assert_select 'a[href]', 3 do |elements|
|
||||
validate_page_numbers [2,3,2], elements
|
||||
assert_select elements.last, ':last-child', "Next »"
|
||||
end
|
||||
assert_select 'span', 2
|
||||
assert_select 'span.disabled:first-child', "« Previous"
|
||||
assert_select 'span.current', entries.current_page.to_s
|
||||
assert_equal '« Previous 1 2 3 Next »', pagination.first.inner_text
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_with_options
|
||||
get :list_developers, { :page => 2 }, :wp => {
|
||||
:class => 'will_paginate', :prev_label => 'Prev', :next_label => 'Next'
|
||||
}
|
||||
assert_response :success
|
||||
|
||||
entries = assigns :developers
|
||||
assert entries
|
||||
assert_equal 4, entries.size
|
||||
|
||||
assert_select 'div.will_paginate', 1, 'no main DIV' do
|
||||
assert_select 'a[href]', 4 do |elements|
|
||||
validate_page_numbers [1,1,3,3], elements
|
||||
# test rel attribute values:
|
||||
assert_select elements[1], 'a', '1' do |link|
|
||||
assert_equal 'prev start', link.first['rel']
|
||||
end
|
||||
assert_select elements.first, 'a', "Prev" do |link|
|
||||
assert_equal 'prev start', link.first['rel']
|
||||
end
|
||||
assert_select elements.last, 'a', "Next" do |link|
|
||||
assert_equal 'next', link.first['rel']
|
||||
end
|
||||
end
|
||||
assert_select 'span.current', entries.current_page.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_without_container
|
||||
get :list_developers, {}, :wp => { :container => false }
|
||||
assert_select 'div.pagination', 0, 'no main DIV'
|
||||
assert_select 'a[href]', 3
|
||||
end
|
||||
|
||||
def test_will_paginate_without_page_links
|
||||
get :list_developers, { :page => 2 }, :wp => { :page_links => false }
|
||||
assert_select 'a[href]', 2 do |elements|
|
||||
validate_page_numbers [1,3], elements
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_preserves_parameters_on_get
|
||||
get :list_developers, :foo => { :bar => 'baz' }
|
||||
assert_links_match /foo%5Bbar%5D=baz/
|
||||
end
|
||||
|
||||
def test_will_paginate_doesnt_preserve_parameters_on_post
|
||||
post :list_developers, :foo => 'bar'
|
||||
assert_no_links_match /foo=bar/
|
||||
end
|
||||
|
||||
def test_adding_additional_parameters
|
||||
get :list_developers, {}, :wp => { :params => { :foo => 'bar' } }
|
||||
assert_links_match /foo=bar/
|
||||
end
|
||||
|
||||
def test_removing_arbitrary_parameters
|
||||
get :list_developers, { :foo => 'bar' }, :wp => { :params => { :foo => nil } }
|
||||
assert_no_links_match /foo=bar/
|
||||
end
|
||||
|
||||
def test_adding_additional_route_parameters
|
||||
get :list_developers, {}, :wp => { :params => { :controller => 'baz' } }
|
||||
assert_links_match %r{\Wbaz/list_developers\W}
|
||||
end
|
||||
|
||||
def test_will_paginate_with_custom_page_param
|
||||
get :list_developers, { :developers_page => 2 }, :wp => { :param_name => :developers_page }
|
||||
assert_response :success
|
||||
|
||||
entries = assigns :developers
|
||||
assert entries
|
||||
assert_equal 4, entries.size
|
||||
|
||||
assert_select 'div.pagination', 1, 'no main DIV' do
|
||||
assert_select 'a[href]', 4 do |elements|
|
||||
validate_page_numbers [1,1,3,3], elements, :developers_page
|
||||
end
|
||||
assert_select 'span.current', entries.current_page.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_windows
|
||||
get :list_developers, { :page => 6, :per_page => 1 }, :wp => { :inner_window => 1 }
|
||||
assert_response :success
|
||||
|
||||
entries = assigns :developers
|
||||
assert entries
|
||||
assert_equal 1, entries.size
|
||||
|
||||
assert_select 'div.pagination', 1, 'no main DIV' do |pagination|
|
||||
assert_select 'a[href]', 8 do |elements|
|
||||
validate_page_numbers [5,1,2,5,7,10,11,7], elements
|
||||
assert_select elements.first, 'a', "« Previous"
|
||||
assert_select elements.last, 'a', "Next »"
|
||||
end
|
||||
assert_select 'span.current', entries.current_page.to_s
|
||||
assert_equal '« Previous 1 2 ... 5 6 7 ... 10 11 Next »', pagination.first.inner_text
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_eliminates_small_gaps
|
||||
get :list_developers, { :page => 6, :per_page => 1 }, :wp => { :inner_window => 2 }
|
||||
assert_response :success
|
||||
|
||||
assert_select 'div.pagination', 1, 'no main DIV' do
|
||||
assert_select 'a[href]', 12 do |elements|
|
||||
validate_page_numbers [5,1,2,3,4,5,7,8,9,10,11,7], elements
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_pagination
|
||||
get :list_developers, :per_page => 12
|
||||
entries = assigns :developers
|
||||
assert_equal 1, entries.page_count
|
||||
assert_equal 11, entries.size
|
||||
|
||||
assert_equal '', @response.body
|
||||
end
|
||||
|
||||
def test_faulty_input_raises_error
|
||||
assert_raise WillPaginate::InvalidPage do
|
||||
get :list_developers, :page => 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
uses_mocha 'helper internals' do
|
||||
def test_collection_name_can_be_guessed
|
||||
collection = mock
|
||||
collection.expects(:page_count).returns(1)
|
||||
get :guess_collection_name, {}, :wp => collection
|
||||
end
|
||||
end
|
||||
|
||||
def test_inferred_collection_name_raises_error_when_nil
|
||||
ex = assert_raise ArgumentError do
|
||||
get :guess_collection_name, {}, :wp => nil
|
||||
end
|
||||
assert ex.message.include?('@developers')
|
||||
end
|
||||
|
||||
def test_setting_id_for_container
|
||||
get :list_developers
|
||||
assert_select 'div.pagination', 1 do |div|
|
||||
assert_nil div.first['id']
|
||||
end
|
||||
# magic ID
|
||||
get :list_developers, {}, :wp => { :id => true }
|
||||
assert_select 'div.pagination', 1 do |div|
|
||||
assert_equal 'fixnums_pagination', div.first['id']
|
||||
end
|
||||
# explicit ID
|
||||
get :list_developers, {}, :wp => { :id => 'custom_id' }
|
||||
assert_select 'div.pagination', 1 do |div|
|
||||
assert_equal 'custom_id', div.first['id']
|
||||
end
|
||||
end
|
||||
|
||||
if ActionController::Base.respond_to? :rescue_responses
|
||||
def test_rescue_response_hook_presence
|
||||
assert_equal :not_found,
|
||||
DevelopersController.rescue_responses['WillPaginate::InvalidPage']
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def validate_page_numbers expected, links, param_name = :page
|
||||
param_pattern = /\W#{param_name}=([^&]*)/
|
||||
|
||||
assert_equal(expected, links.map { |e|
|
||||
e['href'] =~ param_pattern
|
||||
$1 ? $1.to_i : $1
|
||||
})
|
||||
end
|
||||
|
||||
def assert_links_match pattern
|
||||
assert_select 'div.pagination a[href]' do |elements|
|
||||
elements.each do |el|
|
||||
assert_match pattern, el['href']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def assert_no_links_match pattern
|
||||
assert_select 'div.pagination a[href]' do |elements|
|
||||
elements.each do |el|
|
||||
assert_no_match pattern, el['href']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ViewHelpersTest < Test::Unit::TestCase
|
||||
include WillPaginate::ViewHelpers
|
||||
|
||||
def test_page_entries_info
|
||||
arr = ('a'..'z').to_a
|
||||
collection = arr.paginate :page => 2, :per_page => 5
|
||||
assert_equal %{Displaying entries <b>6 - 10</b> of <b>26</b> in total},
|
||||
page_entries_info(collection)
|
||||
|
||||
collection = arr.paginate :page => 7, :per_page => 4
|
||||
assert_equal %{Displaying entries <b>25 - 26</b> of <b>26</b> in total},
|
||||
page_entries_info(collection)
|
||||
end
|
||||
end
|
||||
56
vendor/plugins/will_paginate/test/tasks.rake
vendored
Normal file
56
vendor/plugins/will_paginate/test/tasks.rake
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
require 'rake/testtask'
|
||||
|
||||
desc 'Test the will_paginate plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
t.libs << 'test'
|
||||
end
|
||||
|
||||
# I want to specify environment variables at call time
|
||||
class EnvTestTask < Rake::TestTask
|
||||
attr_accessor :env
|
||||
|
||||
def ruby(*args)
|
||||
env.each { |key, value| ENV[key] = value } if env
|
||||
super
|
||||
env.keys.each { |key| ENV.delete key } if env
|
||||
end
|
||||
end
|
||||
|
||||
for configuration in %w( sqlite3 mysql postgres )
|
||||
EnvTestTask.new("test_#{configuration}") do |t|
|
||||
t.pattern = 'test/finder_test.rb'
|
||||
t.verbose = true
|
||||
t.env = { 'DB' => configuration }
|
||||
t.libs << 'test'
|
||||
end
|
||||
end
|
||||
|
||||
task :test_databases => %w(test_mysql test_sqlite3 test_postgres)
|
||||
|
||||
desc %{Test everything on SQLite3, MySQL and PostgreSQL}
|
||||
task :test_full => %w(test test_mysql test_postgres)
|
||||
|
||||
desc %{Test everything with Rails 1.2.x and 2.0.x gems}
|
||||
task :test_all do
|
||||
all = Rake::Task['test_full']
|
||||
ENV['RAILS_VERSION'] = '~>1.2.6'
|
||||
all.invoke
|
||||
# reset the invoked flag
|
||||
%w( test_full test test_mysql test_postgres ).each do |name|
|
||||
Rake::Task[name].instance_variable_set '@already_invoked', false
|
||||
end
|
||||
# do it again
|
||||
ENV['RAILS_VERSION'] = '~>2.0.2'
|
||||
all.invoke
|
||||
end
|
||||
|
||||
task :rcov do
|
||||
excludes = %w( lib/will_paginate/named_scope*
|
||||
lib/will_paginate/core_ext.rb
|
||||
lib/will_paginate.rb
|
||||
rails* )
|
||||
|
||||
system %[rcov -Itest:lib test/*.rb -x #{excludes.join(',')}]
|
||||
end
|
||||
355
vendor/plugins/will_paginate/test/view_test.rb
vendored
Normal file
355
vendor/plugins/will_paginate/test/view_test.rb
vendored
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
require 'helper'
|
||||
require 'lib/view_test_process'
|
||||
|
||||
class AdditionalLinkAttributesRenderer < WillPaginate::LinkRenderer
|
||||
def initialize(link_attributes = nil)
|
||||
super()
|
||||
@additional_link_attributes = link_attributes || { :default => 'true' }
|
||||
end
|
||||
|
||||
def page_link(page, text, attributes = {})
|
||||
@template.link_to text, url_for(page), attributes.merge(@additional_link_attributes)
|
||||
end
|
||||
end
|
||||
|
||||
class ViewTest < WillPaginate::ViewTestCase
|
||||
|
||||
## basic pagination ##
|
||||
|
||||
def test_will_paginate
|
||||
paginate do |pagination|
|
||||
assert_select 'a[href]', 3 do |elements|
|
||||
validate_page_numbers [2,3,2], elements
|
||||
assert_select elements.last, ':last-child', "Next »"
|
||||
end
|
||||
assert_select 'span', 2
|
||||
assert_select 'span.disabled:first-child', '« Previous'
|
||||
assert_select 'span.current', '1'
|
||||
assert_equal '« Previous 1 2 3 Next »', pagination.first.inner_text
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_pagination_when_page_count_is_one
|
||||
paginate :per_page => 30
|
||||
assert_equal '', @html_result
|
||||
end
|
||||
|
||||
def test_will_paginate_with_options
|
||||
paginate({ :page => 2 },
|
||||
:class => 'will_paginate', :prev_label => 'Prev', :next_label => 'Next') do
|
||||
assert_select 'a[href]', 4 do |elements|
|
||||
validate_page_numbers [1,1,3,3], elements
|
||||
# test rel attribute values:
|
||||
assert_select elements[1], 'a', '1' do |link|
|
||||
assert_equal 'prev start', link.first['rel']
|
||||
end
|
||||
assert_select elements.first, 'a', "Prev" do |link|
|
||||
assert_equal 'prev start', link.first['rel']
|
||||
end
|
||||
assert_select elements.last, 'a', "Next" do |link|
|
||||
assert_equal 'next', link.first['rel']
|
||||
end
|
||||
end
|
||||
assert_select 'span.current', '2'
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_using_renderer_class
|
||||
paginate({}, :renderer => AdditionalLinkAttributesRenderer) do
|
||||
assert_select 'a[default=true]', 3
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_using_renderer_instance
|
||||
renderer = WillPaginate::LinkRenderer.new
|
||||
renderer.gap_marker = '<span class="my-gap">~~</span>'
|
||||
|
||||
paginate({ :per_page => 2 }, :inner_window => 0, :outer_window => 0, :renderer => renderer) do
|
||||
assert_select 'span.my-gap', '~~'
|
||||
end
|
||||
|
||||
renderer = AdditionalLinkAttributesRenderer.new(:title => 'rendered')
|
||||
paginate({}, :renderer => renderer) do
|
||||
assert_select 'a[title=rendered]', 3
|
||||
end
|
||||
end
|
||||
|
||||
def test_prev_next_links_have_classnames
|
||||
paginate do |pagination|
|
||||
assert_select 'span.disabled.prev_page:first-child'
|
||||
assert_select 'a.next_page[href]:last-child'
|
||||
end
|
||||
end
|
||||
|
||||
def test_full_output
|
||||
paginate
|
||||
expected = <<-HTML
|
||||
<div class="pagination"><span class="disabled prev_page">« Previous</span>
|
||||
<span class="current">1</span>
|
||||
<a href="/foo/bar?page=2" rel="next">2</a>
|
||||
<a href="/foo/bar?page=3">3</a>
|
||||
<a href="/foo/bar?page=2" class="next_page" rel="next">Next »</a></div>
|
||||
HTML
|
||||
expected.strip!.gsub!(/\s{2,}/, ' ')
|
||||
|
||||
assert_dom_equal expected, @html_result
|
||||
end
|
||||
|
||||
def test_escaping_of_urls
|
||||
paginate({:page => 1, :per_page => 1, :total_entries => 2},
|
||||
:page_links => false, :params => { :tag => '<br>' })
|
||||
|
||||
assert_select 'a[href]', 1 do |links|
|
||||
query = links.first['href'].split('?', 2)[1]
|
||||
assert_equal %w(page=2 tag=%3Cbr%3E), query.split('&').sort
|
||||
end
|
||||
end
|
||||
|
||||
## advanced options for pagination ##
|
||||
|
||||
def test_will_paginate_without_container
|
||||
paginate({}, :container => false)
|
||||
assert_select 'div.pagination', 0, 'main DIV present when it shouldn\'t'
|
||||
assert_select 'a[href]', 3
|
||||
end
|
||||
|
||||
def test_will_paginate_without_page_links
|
||||
paginate({ :page => 2 }, :page_links => false) do
|
||||
assert_select 'a[href]', 2 do |elements|
|
||||
validate_page_numbers [1,3], elements
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_windows
|
||||
paginate({ :page => 6, :per_page => 1 }, :inner_window => 1) do |pagination|
|
||||
assert_select 'a[href]', 8 do |elements|
|
||||
validate_page_numbers [5,1,2,5,7,10,11,7], elements
|
||||
assert_select elements.first, 'a', '« Previous'
|
||||
assert_select elements.last, 'a', 'Next »'
|
||||
end
|
||||
assert_select 'span.current', '6'
|
||||
assert_equal '« Previous 1 2 … 5 6 7 … 10 11 Next »', pagination.first.inner_text
|
||||
end
|
||||
end
|
||||
|
||||
def test_will_paginate_eliminates_small_gaps
|
||||
paginate({ :page => 6, :per_page => 1 }, :inner_window => 2) do
|
||||
assert_select 'a[href]', 12 do |elements|
|
||||
validate_page_numbers [5,1,2,3,4,5,7,8,9,10,11,7], elements
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_container_id
|
||||
paginate do |div|
|
||||
assert_nil div.first['id']
|
||||
end
|
||||
|
||||
# magic ID
|
||||
paginate({}, :id => true) do |div|
|
||||
assert_equal 'fixnums_pagination', div.first['id']
|
||||
end
|
||||
|
||||
# explicit ID
|
||||
paginate({}, :id => 'custom_id') do |div|
|
||||
assert_equal 'custom_id', div.first['id']
|
||||
end
|
||||
end
|
||||
|
||||
## other helpers ##
|
||||
|
||||
def test_paginated_section
|
||||
@template = <<-ERB
|
||||
<% paginated_section collection, options do %>
|
||||
<%= content_tag :div, '', :id => "developers" %>
|
||||
<% end %>
|
||||
ERB
|
||||
|
||||
paginate
|
||||
assert_select 'div.pagination', 2
|
||||
assert_select 'div.pagination + div#developers', 1
|
||||
end
|
||||
|
||||
def test_page_entries_info
|
||||
@template = '<%= page_entries_info collection %>'
|
||||
array = ('a'..'z').to_a
|
||||
|
||||
paginate array.paginate(:page => 2, :per_page => 5)
|
||||
assert_equal %{Displaying strings <b>6 - 10</b> of <b>26</b> in total},
|
||||
@html_result
|
||||
|
||||
paginate array.paginate(:page => 7, :per_page => 4)
|
||||
assert_equal %{Displaying strings <b>25 - 26</b> of <b>26</b> in total},
|
||||
@html_result
|
||||
end
|
||||
|
||||
def test_page_entries_info_with_longer_class_name
|
||||
@template = '<%= page_entries_info collection %>'
|
||||
collection = ('a'..'z').to_a.paginate
|
||||
collection.first.stubs(:class).returns(mock('class', :name => 'ProjectType'))
|
||||
|
||||
paginate collection
|
||||
assert @html_result.index('project types'), "expected <#{@html_result.inspect}> to mention 'project types'"
|
||||
end
|
||||
|
||||
def test_page_entries_info_with_single_page_collection
|
||||
@template = '<%= page_entries_info collection %>'
|
||||
|
||||
paginate(('a'..'d').to_a.paginate(:page => 1, :per_page => 5))
|
||||
assert_equal %{Displaying <b>all 4</b> strings}, @html_result
|
||||
|
||||
paginate(['a'].paginate(:page => 1, :per_page => 5))
|
||||
assert_equal %{Displaying <b>1</b> string}, @html_result
|
||||
|
||||
paginate([].paginate(:page => 1, :per_page => 5))
|
||||
assert_equal %{No entries found}, @html_result
|
||||
end
|
||||
|
||||
def test_page_entries_info_with_custom_entry_name
|
||||
@template = '<%= page_entries_info collection, :entry_name => "author" %>'
|
||||
|
||||
entries = (1..20).to_a
|
||||
|
||||
paginate(entries.paginate(:page => 1, :per_page => 5))
|
||||
assert_equal %{Displaying authors <b>1 - 5</b> of <b>20</b> in total}, @html_result
|
||||
|
||||
paginate(entries.paginate(:page => 1, :per_page => 20))
|
||||
assert_equal %{Displaying <b>all 20</b> authors}, @html_result
|
||||
|
||||
paginate(['a'].paginate(:page => 1, :per_page => 5))
|
||||
assert_equal %{Displaying <b>1</b> author}, @html_result
|
||||
|
||||
paginate([].paginate(:page => 1, :per_page => 5))
|
||||
assert_equal %{No authors found}, @html_result
|
||||
end
|
||||
|
||||
## parameter handling in page links ##
|
||||
|
||||
def test_will_paginate_preserves_parameters_on_get
|
||||
@request.params :foo => { :bar => 'baz' }
|
||||
paginate
|
||||
assert_links_match /foo%5Bbar%5D=baz/
|
||||
end
|
||||
|
||||
def test_will_paginate_doesnt_preserve_parameters_on_post
|
||||
@request.post
|
||||
@request.params :foo => 'bar'
|
||||
paginate
|
||||
assert_no_links_match /foo=bar/
|
||||
end
|
||||
|
||||
def test_adding_additional_parameters
|
||||
paginate({}, :params => { :foo => 'bar' })
|
||||
assert_links_match /foo=bar/
|
||||
end
|
||||
|
||||
def test_adding_anchor_parameter
|
||||
paginate({}, :params => { :anchor => 'anchor' })
|
||||
assert_links_match /#anchor$/
|
||||
end
|
||||
|
||||
def test_removing_arbitrary_parameters
|
||||
@request.params :foo => 'bar'
|
||||
paginate({}, :params => { :foo => nil })
|
||||
assert_no_links_match /foo=bar/
|
||||
end
|
||||
|
||||
def test_adding_additional_route_parameters
|
||||
paginate({}, :params => { :controller => 'baz', :action => 'list' })
|
||||
assert_links_match %r{\Wbaz/list\W}
|
||||
end
|
||||
|
||||
def test_will_paginate_with_custom_page_param
|
||||
paginate({ :page => 2 }, :param_name => :developers_page) do
|
||||
assert_select 'a[href]', 4 do |elements|
|
||||
validate_page_numbers [1,1,3,3], elements, :developers_page
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_complex_custom_page_param
|
||||
@request.params :developers => { :page => 2 }
|
||||
|
||||
paginate({ :page => 2 }, :param_name => 'developers[page]') do
|
||||
assert_select 'a[href]', 4 do |links|
|
||||
assert_links_match /\?developers%5Bpage%5D=\d+$/, links
|
||||
validate_page_numbers [1,1,3,3], links, 'developers[page]'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_custom_routing_page_param
|
||||
@request.symbolized_path_parameters.update :controller => 'dummy', :action => nil
|
||||
paginate :per_page => 2 do
|
||||
assert_select 'a[href]', 6 do |links|
|
||||
assert_links_match %r{/page/(\d+)$}, links, [2, 3, 4, 5, 6, 2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_custom_routing_page_param_with_dot_separator
|
||||
@request.symbolized_path_parameters.update :controller => 'dummy', :action => 'dots'
|
||||
paginate :per_page => 2 do
|
||||
assert_select 'a[href]', 6 do |links|
|
||||
assert_links_match %r{/page\.(\d+)$}, links, [2, 3, 4, 5, 6, 2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_custom_routing_with_first_page_hidden
|
||||
@request.symbolized_path_parameters.update :controller => 'ibocorp', :action => nil
|
||||
paginate :page => 2, :per_page => 2 do
|
||||
assert_select 'a[href]', 7 do |links|
|
||||
assert_links_match %r{/ibocorp(?:/(\d+))?$}, links, [nil, nil, 3, 4, 5, 6, 3]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
## internal hardcore stuff ##
|
||||
|
||||
class LegacyCollection < WillPaginate::Collection
|
||||
alias :page_count :total_pages
|
||||
undef :total_pages
|
||||
end
|
||||
|
||||
def test_deprecation_notices_with_page_count
|
||||
collection = LegacyCollection.new(1, 1, 2)
|
||||
|
||||
assert_deprecated collection.class.name do
|
||||
paginate collection
|
||||
end
|
||||
end
|
||||
|
||||
uses_mocha 'view internals' do
|
||||
def test_collection_name_can_be_guessed
|
||||
collection = mock
|
||||
collection.expects(:total_pages).returns(1)
|
||||
|
||||
@template = '<%= will_paginate options %>'
|
||||
@controller.controller_name = 'developers'
|
||||
@view.assigns['developers'] = collection
|
||||
|
||||
paginate(nil)
|
||||
end
|
||||
end
|
||||
|
||||
def test_inferred_collection_name_raises_error_when_nil
|
||||
@template = '<%= will_paginate options %>'
|
||||
@controller.controller_name = 'developers'
|
||||
|
||||
e = assert_raise ArgumentError do
|
||||
paginate(nil)
|
||||
end
|
||||
assert e.message.include?('@developers')
|
||||
end
|
||||
|
||||
if ActionController::Base.respond_to? :rescue_responses
|
||||
# only on Rails 2
|
||||
def test_rescue_response_hook_presence
|
||||
assert_equal :not_found,
|
||||
ActionController::Base.rescue_responses['WillPaginate::InvalidPage']
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
21
vendor/plugins/will_paginate/will_paginate.gemspec
vendored
Normal file
21
vendor/plugins/will_paginate/will_paginate.gemspec
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
Gem::Specification.new do |s|
|
||||
s.name = 'will_paginate'
|
||||
s.version = '2.3.2'
|
||||
s.date = '2008-05-16'
|
||||
|
||||
s.summary = "Most awesome pagination solution for Rails"
|
||||
s.description = "The will_paginate library provides a simple, yet powerful and extensible API for ActiveRecord pagination and rendering of pagination links in ActionView templates."
|
||||
|
||||
s.authors = ['Mislav Marohnić', 'PJ Hyett']
|
||||
s.email = 'mislav.marohnic@gmail.com'
|
||||
s.homepage = 'http://github.com/mislav/will_paginate/wikis'
|
||||
|
||||
s.has_rdoc = true
|
||||
s.rdoc_options = ['--main', 'README.rdoc']
|
||||
s.rdoc_options << '--inline-source' << '--charset=UTF-8'
|
||||
s.extra_rdoc_files = ['README.rdoc', 'LICENSE', 'CHANGELOG']
|
||||
s.add_dependency 'activesupport', ['>= 1.4.4']
|
||||
|
||||
s.files = %w(CHANGELOG LICENSE README.rdoc Rakefile examples examples/apple-circle.gif examples/index.haml examples/index.html examples/pagination.css examples/pagination.sass init.rb lib lib/will_paginate lib/will_paginate.rb lib/will_paginate/array.rb lib/will_paginate/collection.rb lib/will_paginate/core_ext.rb lib/will_paginate/finder.rb lib/will_paginate/named_scope.rb lib/will_paginate/named_scope_patch.rb lib/will_paginate/version.rb lib/will_paginate/view_helpers.rb test test/boot.rb test/collection_test.rb test/console test/database.yml test/finder_test.rb test/fixtures test/fixtures/admin.rb test/fixtures/developer.rb test/fixtures/developers_projects.yml test/fixtures/project.rb test/fixtures/projects.yml test/fixtures/replies.yml test/fixtures/reply.rb test/fixtures/schema.rb test/fixtures/topic.rb test/fixtures/topics.yml test/fixtures/user.rb test/fixtures/users.yml test/helper.rb test/lib test/lib/activerecord_test_case.rb test/lib/activerecord_test_connector.rb test/lib/load_fixtures.rb test/lib/view_test_process.rb test/tasks.rake test/view_test.rb)
|
||||
s.test_files = %w(test/boot.rb test/collection_test.rb test/console test/database.yml test/finder_test.rb test/fixtures test/fixtures/admin.rb test/fixtures/developer.rb test/fixtures/developers_projects.yml test/fixtures/project.rb test/fixtures/projects.yml test/fixtures/replies.yml test/fixtures/reply.rb test/fixtures/schema.rb test/fixtures/topic.rb test/fixtures/topics.yml test/fixtures/user.rb test/fixtures/users.yml test/helper.rb test/lib test/lib/activerecord_test_case.rb test/lib/activerecord_test_connector.rb test/lib/load_fixtures.rb test/lib/view_test_process.rb test/tasks.rake test/view_test.rb)
|
||||
end
|
||||
14
vendor/rails/.gitignore
vendored
Normal file
14
vendor/rails/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
debug.log
|
||||
activeresource/doc
|
||||
activerecord/doc
|
||||
actionpack/doc
|
||||
actionmailer/doc
|
||||
activesupport/doc
|
||||
railties/doc
|
||||
activeresource/pkg
|
||||
activerecord/pkg
|
||||
actionpack/pkg
|
||||
actionmailer/pkg
|
||||
activesupport/pkg
|
||||
railties/pkg
|
||||
*.rbc
|
||||
2
vendor/rails/Rakefile
vendored
2
vendor/rails/Rakefile
vendored
|
|
@ -15,7 +15,7 @@ task :default => :test
|
|||
desc "Run #{task_name} task for all projects"
|
||||
task task_name do
|
||||
PROJECTS.each do |project|
|
||||
system %(cd #{project} && #{env} rake #{task_name})
|
||||
system %(cd #{project} && #{env} #{$0} #{task_name})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
11
vendor/rails/actionmailer/CHANGELOG
vendored
11
vendor/rails/actionmailer/CHANGELOG
vendored
|
|
@ -1,3 +1,14 @@
|
|||
*2.1.0 (May 31st, 2008)*
|
||||
|
||||
* Fixed that a return-path header would be ignored #7572 [joost]
|
||||
|
||||
* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 [iaddict, Tarmo Tänav]
|
||||
|
||||
* Updated TMail to version 1.2.1 [raasdnil]
|
||||
|
||||
* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
|
||||
|
||||
|
||||
*2.0.2* (December 16th, 2007)
|
||||
|
||||
* Included in Rails 2.0.2
|
||||
|
|
|
|||
2
vendor/rails/actionmailer/MIT-LICENSE
vendored
2
vendor/rails/actionmailer/MIT-LICENSE
vendored
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2004-2007 David Heinemeier Hansson
|
||||
Copyright (c) 2004-2008 David Heinemeier Hansson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
|
|
|||
24
vendor/rails/actionmailer/README
vendored
24
vendor/rails/actionmailer/README
vendored
|
|
@ -19,8 +19,7 @@ are all set up this way. An example of such a method:
|
|||
recipients recipient
|
||||
subject "[Signed up] Welcome #{recipient}"
|
||||
from "system@loudthinking.com"
|
||||
|
||||
body(:recipient => recipient)
|
||||
body :recipient => recipient
|
||||
end
|
||||
|
||||
The body of the email is created by using an Action View template (regular
|
||||
|
|
@ -78,21 +77,26 @@ Example:
|
|||
end
|
||||
end
|
||||
|
||||
This Mailman can be the target for Postfix. In Rails, you would use the runner like this:
|
||||
This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the
|
||||
trivial case like this:
|
||||
|
||||
./script/runner 'Mailman.receive(STDIN.read)'
|
||||
|
||||
However, invoking Rails in the runner for each mail to be received is very resource intensive. A single
|
||||
instance of Rails should be run within a daemon if it is going to be utilized to process more than just
|
||||
a limited number of email.
|
||||
|
||||
== Configuration
|
||||
|
||||
The Base class has the full list of configuration options. Here's an example:
|
||||
|
||||
ActionMailer::Base.smtp_settings = {
|
||||
:address=>'smtp.yourserver.com', # default: localhost
|
||||
:port=>'25', # default: 25
|
||||
:user_name=>'user',
|
||||
:password=>'pass',
|
||||
:authentication=>:plain # :plain, :login or :cram_md5
|
||||
}
|
||||
ActionMailer::Base.smtp_settings = {
|
||||
:address => 'smtp.yourserver.com', # default: localhost
|
||||
:port => '25', # default: 25
|
||||
:user_name => 'user',
|
||||
:password => 'pass',
|
||||
:authentication => :plain # :plain, :login or :cram_md5
|
||||
}
|
||||
|
||||
== Dependencies
|
||||
|
||||
|
|
|
|||
5
vendor/rails/actionmailer/Rakefile
vendored
5
vendor/rails/actionmailer/Rakefile
vendored
|
|
@ -4,7 +4,7 @@ require 'rake/testtask'
|
|||
require 'rake/rdoctask'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
require 'rake/contrib/sshpublisher'
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
|
|
@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s|
|
|||
s.rubyforge_project = "actionmailer"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 2.0.2' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.1.0' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
|
@ -87,6 +87,7 @@ end
|
|||
desc "Publish the release files to RubyForge."
|
||||
task :release => [ :package ] do
|
||||
require 'rubyforge'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
|
||||
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#--
|
||||
# Copyright (c) 2004-2007 David Heinemeier Hansson
|
||||
# Copyright (c) 2004-2008 David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ module ActionMailer
|
|||
define_method(name) do |*parameters|
|
||||
raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
|
||||
if parameters.empty?
|
||||
if instance_variables.include?(ivar)
|
||||
if instance_variable_names.include?(ivar)
|
||||
instance_variable_get(ivar)
|
||||
end
|
||||
else
|
||||
|
|
|
|||
149
vendor/rails/actionmailer/lib/action_mailer/base.rb
vendored
149
vendor/rails/actionmailer/lib/action_mailer/base.rb
vendored
|
|
@ -5,12 +5,12 @@ require 'action_mailer/utils'
|
|||
require 'tmail/net'
|
||||
|
||||
module ActionMailer #:nodoc:
|
||||
# ActionMailer allows you to send email from your application using a mailer model and views.
|
||||
# Action Mailer allows you to send email from your application using a mailer model and views.
|
||||
#
|
||||
#
|
||||
# = Mailer Models
|
||||
#
|
||||
# To use ActionMailer, you need to create a mailer model.
|
||||
# To use Action Mailer, you need to create a mailer model.
|
||||
#
|
||||
# $ script/generate mailer Notifier
|
||||
#
|
||||
|
|
@ -35,22 +35,27 @@ module ActionMailer #:nodoc:
|
|||
# * <tt>subject</tt> - The subject of your email. Sets the <tt>Subject:</tt> header.
|
||||
# * <tt>from</tt> - Who the email you are sending is from. Sets the <tt>From:</tt> header.
|
||||
# * <tt>cc</tt> - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the <tt>Cc:</tt> header.
|
||||
# * <tt>bcc</tt> - Takes one or more email address. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc</tt> header.
|
||||
# * <tt>bcc</tt> - Takes one or more email addresses. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc:</tt> header.
|
||||
# * <tt>reply_to</tt> - Takes one or more email addresses. These addresses will be listed as the default recipients when replying to your email. Sets the <tt>Reply-To:</tt> header.
|
||||
# * <tt>sent_on</tt> - The date on which the message was sent. If not set, the header wil be set by the delivery agent.
|
||||
# * <tt>content_type</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
|
||||
# * <tt>headers</tt> - Specify additional headers to be set for the message, e.g. <tt>headers 'X-Mail-Count' => 107370</tt>.
|
||||
#
|
||||
# When a <tt>headers 'return-path'</tt> is specified, that value will be used as the 'envelope from'
|
||||
# address. Setting this is useful when you want delivery notifications sent to a different address than
|
||||
# the one in <tt>from</tt>.
|
||||
#
|
||||
# The <tt>body</tt> method has special behavior. It takes a hash which generates an instance variable
|
||||
# named after each key in the hash containing the value that that key points to.
|
||||
#
|
||||
# So, for example, <tt>body "account" => recipient</tt> would result
|
||||
# So, for example, <tt>body :account => recipient</tt> would result
|
||||
# in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
|
||||
# view.
|
||||
#
|
||||
#
|
||||
# = Mailer views
|
||||
#
|
||||
# Like ActionController, each mailer class has a corresponding view directory
|
||||
# Like Action Controller, each mailer class has a corresponding view directory
|
||||
# in which each method of the class looks for a template with its name.
|
||||
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same name as the method
|
||||
# in your mailer model. For example, in the mailer defined above, the template at
|
||||
|
|
@ -69,21 +74,36 @@ module ActionMailer #:nodoc:
|
|||
# <%= truncate(note.body, 25) %>
|
||||
#
|
||||
#
|
||||
# = Generating URLs for mailer views
|
||||
# = Generating URLs
|
||||
#
|
||||
# If your view includes URLs from the application, you need to use url_for in the mailing method instead of the view.
|
||||
# Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request. That's
|
||||
# why you need to jump this little hoop and supply all the details needed for the URL. Example:
|
||||
# URLs can be generated in mailer views using <tt>url_for</tt> or named routes.
|
||||
# Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request,
|
||||
# so you'll need to provide all of the details needed to generate a URL.
|
||||
#
|
||||
# def signup_notification(recipient)
|
||||
# recipients recipient.email_address_with_name
|
||||
# from "system@example.com"
|
||||
# subject "New account information"
|
||||
# body :account => recipient,
|
||||
# :home_page => url_for(:host => "example.com", :controller => "welcome", :action => "greeting")
|
||||
# end
|
||||
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
|
||||
#
|
||||
# You can now access @home_page in the template and get http://example.com/welcome/greeting.
|
||||
# <%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
|
||||
#
|
||||
# When using named routes you only need to supply the <tt>:host</tt>:
|
||||
#
|
||||
# <%= users_url(:host => "example.com") %>
|
||||
#
|
||||
# You will want to avoid using the <tt>name_of_route_path</tt> form of named routes because it doesn't make sense to
|
||||
# generate relative URLs in email messages.
|
||||
#
|
||||
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt> option in
|
||||
# the <tt>ActionMailer::Base.default_url_options</tt> hash as follows:
|
||||
#
|
||||
# ActionMailer::Base.default_url_options[:host] = "example.com"
|
||||
#
|
||||
# This can also be set as a configuration option in <tt>config/environment.rb</tt>:
|
||||
#
|
||||
# config.action_mailer.default_url_options = { :host => "example.com" }
|
||||
#
|
||||
# If you do decide to set a default <tt>:host</tt> for your mailers you will want to use the
|
||||
# <tt>:only_path => false</tt> option when using <tt>url_for</tt>. This will ensure that absolute URLs are generated because
|
||||
# the <tt>url_for</tt> view helper will, by default, generate relative URLs when a <tt>:host</tt> option isn't
|
||||
# explicitly provided.
|
||||
#
|
||||
# = Sending mail
|
||||
#
|
||||
|
|
@ -110,9 +130,9 @@ module ActionMailer #:nodoc:
|
|||
# def signup_notification(recipient)
|
||||
# recipients recipient.email_address_with_name
|
||||
# subject "New account information"
|
||||
# body "account" => recipient
|
||||
# from "system@example.com"
|
||||
# content_type "text/html" # Here's where the magic happens
|
||||
# body :account => recipient
|
||||
# content_type "text/html"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
|
|
@ -126,6 +146,7 @@ module ActionMailer #:nodoc:
|
|||
# recipients recipient.email_address_with_name
|
||||
# subject "New account information"
|
||||
# from "system@example.com"
|
||||
# content_type "multipart/alternative"
|
||||
#
|
||||
# part :content_type => "text/html",
|
||||
# :body => render_message("signup-as-html", :account => recipient)
|
||||
|
|
@ -137,7 +158,7 @@ module ActionMailer #:nodoc:
|
|||
# end
|
||||
# end
|
||||
#
|
||||
# Multipart messages can also be used implicitly because ActionMailer will automatically
|
||||
# Multipart messages can also be used implicitly because Action Mailer will automatically
|
||||
# detect and use multipart templates, where each template is named after the name of the action, followed
|
||||
# by the content type. Each such detected template will be added as separate part to the message.
|
||||
#
|
||||
|
|
@ -148,9 +169,14 @@ module ActionMailer #:nodoc:
|
|||
# * signup_notification.text.x-yaml.erb
|
||||
#
|
||||
# Each would be rendered and added as a separate part to the message,
|
||||
# with the corresponding content type. The same body hash is passed to
|
||||
# each template.
|
||||
# with the corresponding content type. The content type for the entire
|
||||
# message is automatically set to <tt>multipart/alternative</tt>, which indicates
|
||||
# that the email contains multiple different representations of the same email
|
||||
# body. The same body hash is passed to each template.
|
||||
#
|
||||
# Implicit template rendering is not performed if any attachments or parts have been added to the email.
|
||||
# This means that you'll have to manually add each part to the email and set the content type of the email
|
||||
# to <tt>multipart/alternative</tt>.
|
||||
#
|
||||
# = Attachments
|
||||
#
|
||||
|
|
@ -179,44 +205,45 @@ module ActionMailer #:nodoc:
|
|||
#
|
||||
# These options are specified on the class level, like <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
|
||||
#
|
||||
# * <tt>template_root</tt> - template root determines the base from which template references will be made.
|
||||
# * <tt>template_root</tt> - Determines the base from which template references will be made.
|
||||
#
|
||||
# * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
|
||||
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
|
||||
#
|
||||
# * <tt>smtp_settings</tt> - Allows detailed configuration for :smtp delivery method:
|
||||
# * <tt>:address</tt> Allows you to use a remote mail server. Just change it from its default "localhost" setting.
|
||||
# * <tt>:port</tt> On the off chance that your mail server doesn't run on port 25, you can change it.
|
||||
# * <tt>:domain</tt> If you need to specify a HELO domain, you can do it here.
|
||||
# * <tt>:user_name</tt> If your mail server requires authentication, set the username in this setting.
|
||||
# * <tt>:password</tt> If your mail server requires authentication, set the password in this setting.
|
||||
# * <tt>:authentication</tt> If your mail server requires authentication, you need to specify the authentication type here.
|
||||
# This is a symbol and one of :plain, :login, :cram_md5
|
||||
# * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
|
||||
# * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
|
||||
# * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
|
||||
# * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
|
||||
# * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
|
||||
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
|
||||
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here.
|
||||
# This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
|
||||
#
|
||||
# * <tt>sendmail_settings</tt> - Allows you to override options for the :sendmail delivery method
|
||||
# * <tt>:location</tt> The location of the sendmail executable, defaults to "/usr/sbin/sendmail"
|
||||
# * <tt>:arguments</tt> The command line arguments
|
||||
# * <tt>raise_delivery_errors</tt> - whether or not errors should be raised if the email fails to be delivered.
|
||||
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
|
||||
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
|
||||
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt>.
|
||||
#
|
||||
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are :smtp (default), :sendmail, and :test.
|
||||
# * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
|
||||
#
|
||||
# * <tt>perform_deliveries</tt> - Determines whether deliver_* methods are actually carried out. By default they are,
|
||||
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, and <tt>:test</tt>.
|
||||
#
|
||||
# * <tt>perform_deliveries</tt> - Determines whether <tt>deliver_*</tt> methods are actually carried out. By default they are,
|
||||
# but this can be turned off to help functional testing.
|
||||
#
|
||||
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful
|
||||
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with <tt>delivery_method :test</tt>. Most useful
|
||||
# for unit and functional testing.
|
||||
#
|
||||
# * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
|
||||
# pick a different charset from inside a method with <tt>@charset</tt>.
|
||||
# pick a different charset from inside a method with +charset+.
|
||||
# * <tt>default_content_type</tt> - The default content type used for the main part of the message. Defaults to "text/plain". You
|
||||
# can also pick a different content type from inside a method with <tt>@content_type</tt>.
|
||||
# * <tt>default_mime_version</tt> - The default mime version used for the message. Defaults to "1.0". You
|
||||
# can also pick a different value from inside a method with <tt>@mime_version</tt>.
|
||||
# can also pick a different content type from inside a method with +content_type+.
|
||||
# * <tt>default_mime_version</tt> - The default mime version used for the message. Defaults to <tt>1.0</tt>. You
|
||||
# can also pick a different value from inside a method with +mime_version+.
|
||||
# * <tt>default_implicit_parts_order</tt> - When a message is built implicitly (i.e. multiple parts are assembled from templates
|
||||
# which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
|
||||
# ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client
|
||||
# <tt>["text/html", "text/enriched", "text/plain"]</tt>. Items that appear first in the array have higher priority in the mail client
|
||||
# and appear last in the mime encoded message. You can also pick a different order from inside a method with
|
||||
# <tt>@implicit_parts_order</tt>.
|
||||
# +implicit_parts_order+.
|
||||
class Base
|
||||
include AdvAttrAccessor, PartContainer
|
||||
include ActionController::UrlWriter if Object.const_defined?(:ActionController)
|
||||
|
|
@ -291,6 +318,10 @@ module ActionMailer #:nodoc:
|
|||
# Specify the from address for the message.
|
||||
adv_attr_accessor :from
|
||||
|
||||
# Specify the address (if different than the "from" address) to direct
|
||||
# replies to this message.
|
||||
adv_attr_accessor :reply_to
|
||||
|
||||
# Specify additional headers to be added to the message.
|
||||
adv_attr_accessor :headers
|
||||
|
||||
|
|
@ -357,8 +388,8 @@ module ActionMailer #:nodoc:
|
|||
|
||||
# Receives a raw email, parses it into an email object, decodes it,
|
||||
# instantiates a new mailer, and passes the email object to the mailer
|
||||
# object's #receive method. If you want your mailer to be able to
|
||||
# process incoming messages, you'll need to implement a #receive
|
||||
# object's +receive+ method. If you want your mailer to be able to
|
||||
# process incoming messages, you'll need to implement a +receive+
|
||||
# method that accepts the email object as a parameter:
|
||||
#
|
||||
# class MyMailer < ActionMailer::Base
|
||||
|
|
@ -387,12 +418,17 @@ module ActionMailer #:nodoc:
|
|||
# templating language other than rhtml or rxml are supported.
|
||||
# To use this, include in your template-language plugin's init
|
||||
# code or on a per-application basis, this can be invoked from
|
||||
# config/environment.rb:
|
||||
# <tt>config/environment.rb</tt>:
|
||||
#
|
||||
# ActionMailer::Base.register_template_extension('haml')
|
||||
def register_template_extension(extension)
|
||||
template_extensions << extension
|
||||
end
|
||||
|
||||
def template_root=(root)
|
||||
write_inheritable_attribute(:template_root, root)
|
||||
ActionView::TemplateFinder.process_view_paths(root)
|
||||
end
|
||||
end
|
||||
|
||||
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
|
||||
|
|
@ -459,11 +495,14 @@ module ActionMailer #:nodoc:
|
|||
end
|
||||
|
||||
# Delivers a TMail::Mail object. By default, it delivers the cached mail
|
||||
# object (from the #create! method). If no cached mail object exists, and
|
||||
# object (from the <tt>create!</tt> method). If no cached mail object exists, and
|
||||
# no alternate has been given as the parameter, this will fail.
|
||||
def deliver!(mail = @mail)
|
||||
raise "no mail object available for delivery!" unless mail
|
||||
logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
|
||||
unless logger.nil?
|
||||
logger.info "Sent mail to #{Array(recipients).join(', ')}"
|
||||
logger.debug "\n#{mail.encoded}"
|
||||
end
|
||||
|
||||
begin
|
||||
__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
|
||||
|
|
@ -483,7 +522,7 @@ module ActionMailer #:nodoc:
|
|||
@content_type ||= @@default_content_type.dup
|
||||
@implicit_parts_order ||= @@default_implicit_parts_order.dup
|
||||
@template ||= method_name
|
||||
@mailer_name ||= Inflector.underscore(self.class.name)
|
||||
@mailer_name ||= self.class.name.underscore
|
||||
@parts ||= []
|
||||
@headers ||= {}
|
||||
@body ||= {}
|
||||
|
|
@ -546,9 +585,10 @@ module ActionMailer #:nodoc:
|
|||
m.to, m.from = quote_any_address_if_necessary(charset, recipients, from)
|
||||
m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil?
|
||||
m.cc = quote_address_if_necessary(cc, charset) unless cc.nil?
|
||||
|
||||
m.reply_to = quote_address_if_necessary(reply_to, charset) unless reply_to.nil?
|
||||
m.mime_version = mime_version unless mime_version.nil?
|
||||
m.date = sent_on.to_time rescue sent_on if sent_on
|
||||
|
||||
headers.each { |k, v| m[k] = v }
|
||||
|
||||
real_content_type, ctype_attrs = parse_content_type
|
||||
|
|
@ -582,15 +622,18 @@ module ActionMailer #:nodoc:
|
|||
def perform_delivery_smtp(mail)
|
||||
destinations = mail.destinations
|
||||
mail.ready_to_send
|
||||
sender = mail['return-path'] || mail.from
|
||||
|
||||
Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain],
|
||||
smtp_settings[:user_name], smtp_settings[:password], smtp_settings[:authentication]) do |smtp|
|
||||
smtp.sendmail(mail.encoded, mail.from, destinations)
|
||||
smtp.sendmail(mail.encoded, sender, destinations)
|
||||
end
|
||||
end
|
||||
|
||||
def perform_delivery_sendmail(mail)
|
||||
IO.popen("#{sendmail_settings[:location]} #{sendmail_settings[:arguments]}","w+") do |sm|
|
||||
sendmail_args = sendmail_settings[:arguments]
|
||||
sendmail_args += " -f \"#{mail['return-path']}\"" if mail['return-path']
|
||||
IO.popen("#{sendmail_settings[:location]} #{sendmail_args}","w+") do |sm|
|
||||
sm.print(mail.encoded.gsub(/\r/, ''))
|
||||
sm.flush
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module ActionMailer
|
|||
# helper FooHelper
|
||||
# includes FooHelper in the template class.
|
||||
# helper { def foo() "#{bar} is the very best" end }
|
||||
# evaluates the block in the template class, adding method #foo.
|
||||
# evaluates the block in the template class, adding method +foo+.
|
||||
# helper(:three, BlindHelper) { def mice() 'mice' end }
|
||||
# does all three.
|
||||
def helper(*args, &block)
|
||||
|
|
@ -93,9 +93,9 @@ module ActionMailer
|
|||
begin
|
||||
child.master_helper_module = Module.new
|
||||
child.master_helper_module.send! :include, master_helper_module
|
||||
child.helper child.name.underscore
|
||||
child.helper child.name.to_s.underscore
|
||||
rescue MissingSourceFile => e
|
||||
raise unless e.is_missing?("helpers/#{child.name.underscore}_helper")
|
||||
raise unless e.is_missing?("helpers/#{child.name.to_s.underscore}_helper")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ require 'action_mailer/utils'
|
|||
module ActionMailer
|
||||
# Represents a subpart of an email message. It shares many similar
|
||||
# attributes of ActionMailer::Base. Although you can create parts manually
|
||||
# and add them to the #parts list of the mailer, it is easier
|
||||
# and add them to the +parts+ list of the mailer, it is easier
|
||||
# to use the helper methods in ActionMailer::PartContainer.
|
||||
class Part
|
||||
include ActionMailer::AdvAttrAccessor
|
||||
|
|
@ -13,7 +13,7 @@ module ActionMailer
|
|||
|
||||
# Represents the body of the part, as a string. This should not be a
|
||||
# Hash (like ActionMailer::Base), but if you want a template to be rendered
|
||||
# into the body of a subpart you can do it with the mailer's #render method
|
||||
# into the body of a subpart you can do it with the mailer's +render+ method
|
||||
# and assign the result here.
|
||||
adv_attr_accessor :body
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ module ActionMailer
|
|||
|
||||
# Quote the given text if it contains any "illegal" characters
|
||||
def quote_if_necessary(text, charset)
|
||||
text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
|
||||
|
||||
(text =~ CHARS_NEEDING_QUOTING) ?
|
||||
quoted_printable(text, charset) :
|
||||
text
|
||||
|
|
@ -38,7 +40,7 @@ module ActionMailer
|
|||
# regular email address, or it can be a phrase followed by an address in
|
||||
# brackets. The phrase is the only part that will be quoted, and only if
|
||||
# it needs to be. This allows extended characters to be used in the
|
||||
# "to", "from", "cc", and "bcc" headers.
|
||||
# "to", "from", "cc", "bcc" and "reply-to" headers.
|
||||
def quote_address_if_necessary(address, charset)
|
||||
if Array === address
|
||||
address.map { |a| quote_address_if_necessary(a, charset) }
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@ module ActionMailer
|
|||
"test case definition"
|
||||
end
|
||||
end
|
||||
# New Test Super class for forward compatibility.
|
||||
# To override
|
||||
|
||||
class TestCase < ActiveSupport::TestCase
|
||||
include ActionMailer::Quoting
|
||||
|
||||
setup :initialize_test_deliveries
|
||||
setup :set_expected_mail
|
||||
|
||||
class << self
|
||||
def tests(mailer)
|
||||
write_inheritable_attribute(:mailer_class, mailer)
|
||||
|
|
@ -33,11 +35,14 @@ module ActionMailer
|
|||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
protected
|
||||
def initialize_test_deliveries
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries = []
|
||||
end
|
||||
|
||||
def set_expected_mail
|
||||
@expected = TMail::Mail.new
|
||||
@expected.set_content_type "text", "plain", { "charset" => charset }
|
||||
@expected.mime_version = '1.0'
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
require 'rubygems'
|
||||
|
||||
begin
|
||||
gem 'tmail', '~> 1.1.0'
|
||||
gem 'tmail', '~> 1.2.3'
|
||||
rescue Gem::LoadError
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.1.0"
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.2.3"
|
||||
end
|
||||
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
#
|
||||
# lib/tmail/Makefile
|
||||
#
|
||||
|
||||
debug:
|
||||
rm -f parser.rb
|
||||
make parser.rb DEBUG=true
|
||||
|
||||
parser.rb: parser.y
|
||||
if [ "$(DEBUG)" = true ]; then \
|
||||
racc -v -g -o$@ parser.y ;\
|
||||
else \
|
||||
racc -E -o$@ parser.y ;\
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -f parser.rb parser.output
|
||||
|
||||
distclean: clean
|
||||
|
|
@ -1,245 +0,0 @@
|
|||
=begin rdoc
|
||||
|
||||
= Address handling class
|
||||
|
||||
=end
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/encode'
|
||||
require 'tmail/parser'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Address
|
||||
|
||||
include TextUtils
|
||||
|
||||
def Address.parse( str )
|
||||
Parser.parse :ADDRESS, str
|
||||
end
|
||||
|
||||
def address_group?
|
||||
false
|
||||
end
|
||||
|
||||
def initialize( local, domain )
|
||||
if domain
|
||||
domain.each do |s|
|
||||
raise SyntaxError, 'empty word in domain' if s.empty?
|
||||
end
|
||||
end
|
||||
|
||||
@local = local
|
||||
@domain = domain
|
||||
@name = nil
|
||||
@routes = []
|
||||
end
|
||||
|
||||
attr_reader :name
|
||||
|
||||
def name=( str )
|
||||
@name = str
|
||||
@name = nil if str and str.empty?
|
||||
end
|
||||
|
||||
alias phrase name
|
||||
alias phrase= name=
|
||||
|
||||
attr_reader :routes
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} #{address()}>"
|
||||
end
|
||||
|
||||
def local
|
||||
return nil unless @local
|
||||
return '""' if @local.size == 1 and @local[0].empty?
|
||||
@local.map {|i| quote_atom(i) }.join('.')
|
||||
end
|
||||
|
||||
def domain
|
||||
return nil unless @domain
|
||||
join_domain(@domain)
|
||||
end
|
||||
|
||||
def spec
|
||||
s = self.local
|
||||
d = self.domain
|
||||
if s and d
|
||||
s + '@' + d
|
||||
else
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
alias address spec
|
||||
|
||||
def ==( other )
|
||||
other.respond_to? :spec and self.spec == other.spec
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def hash
|
||||
@local.hash ^ @domain.hash
|
||||
end
|
||||
|
||||
def dup
|
||||
obj = self.class.new(@local.dup, @domain.dup)
|
||||
obj.name = @name.dup if @name
|
||||
obj.routes.replace @routes
|
||||
obj
|
||||
end
|
||||
|
||||
include StrategyInterface
|
||||
|
||||
def accept( strategy, dummy1 = nil, dummy2 = nil )
|
||||
unless @local
|
||||
strategy.meta '<>' # empty return-path
|
||||
return
|
||||
end
|
||||
|
||||
spec_p = (not @name and @routes.empty?)
|
||||
if @name
|
||||
strategy.phrase @name
|
||||
strategy.space
|
||||
end
|
||||
tmp = spec_p ? '' : '<'
|
||||
unless @routes.empty?
|
||||
tmp << @routes.map {|i| '@' + i }.join(',') << ':'
|
||||
end
|
||||
tmp << self.spec
|
||||
tmp << '>' unless spec_p
|
||||
strategy.meta tmp
|
||||
strategy.lwsp ''
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class AddressGroup
|
||||
|
||||
include Enumerable
|
||||
|
||||
def address_group?
|
||||
true
|
||||
end
|
||||
|
||||
def initialize( name, addrs )
|
||||
@name = name
|
||||
@addresses = addrs
|
||||
end
|
||||
|
||||
attr_reader :name
|
||||
|
||||
def ==( other )
|
||||
other.respond_to? :to_a and @addresses == other.to_a
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def hash
|
||||
map {|i| i.hash }.hash
|
||||
end
|
||||
|
||||
def []( idx )
|
||||
@addresses[idx]
|
||||
end
|
||||
|
||||
def size
|
||||
@addresses.size
|
||||
end
|
||||
|
||||
def empty?
|
||||
@addresses.empty?
|
||||
end
|
||||
|
||||
def each( &block )
|
||||
@addresses.each(&block)
|
||||
end
|
||||
|
||||
def to_a
|
||||
@addresses.dup
|
||||
end
|
||||
|
||||
alias to_ary to_a
|
||||
|
||||
def include?( a )
|
||||
@addresses.include? a
|
||||
end
|
||||
|
||||
def flatten
|
||||
set = []
|
||||
@addresses.each do |a|
|
||||
if a.respond_to? :flatten
|
||||
set.concat a.flatten
|
||||
else
|
||||
set.push a
|
||||
end
|
||||
end
|
||||
set
|
||||
end
|
||||
|
||||
def each_address( &block )
|
||||
flatten.each(&block)
|
||||
end
|
||||
|
||||
def add( a )
|
||||
@addresses.push a
|
||||
end
|
||||
|
||||
alias push add
|
||||
|
||||
def delete( a )
|
||||
@addresses.delete a
|
||||
end
|
||||
|
||||
include StrategyInterface
|
||||
|
||||
def accept( strategy, dummy1 = nil, dummy2 = nil )
|
||||
strategy.phrase @name
|
||||
strategy.meta ':'
|
||||
strategy.space
|
||||
first = true
|
||||
each do |mbox|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
strategy.meta ','
|
||||
end
|
||||
strategy.space
|
||||
mbox.accept strategy
|
||||
end
|
||||
strategy.meta ';'
|
||||
strategy.lwsp ''
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
|
|
@ -1,552 +0,0 @@
|
|||
#
|
||||
# facade.rb
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/utils'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Mail
|
||||
|
||||
def header_string( name, default = nil )
|
||||
h = @header[name.downcase] or return default
|
||||
h.to_s
|
||||
end
|
||||
|
||||
###
|
||||
### attributes
|
||||
###
|
||||
|
||||
include TextUtils
|
||||
|
||||
def set_string_array_attr( key, strs )
|
||||
strs.flatten!
|
||||
if strs.empty?
|
||||
@header.delete key.downcase
|
||||
else
|
||||
store key, strs.join(', ')
|
||||
end
|
||||
strs
|
||||
end
|
||||
private :set_string_array_attr
|
||||
|
||||
def set_string_attr( key, str )
|
||||
if str
|
||||
store key, str
|
||||
else
|
||||
@header.delete key.downcase
|
||||
end
|
||||
str
|
||||
end
|
||||
private :set_string_attr
|
||||
|
||||
def set_addrfield( name, arg )
|
||||
if arg
|
||||
h = HeaderField.internal_new(name, @config)
|
||||
h.addrs.replace [arg].flatten
|
||||
@header[name] = h
|
||||
else
|
||||
@header.delete name
|
||||
end
|
||||
arg
|
||||
end
|
||||
private :set_addrfield
|
||||
|
||||
def addrs2specs( addrs )
|
||||
return nil unless addrs
|
||||
list = addrs.map {|addr|
|
||||
if addr.address_group?
|
||||
then addr.map {|a| a.spec }
|
||||
else addr.spec
|
||||
end
|
||||
}.flatten
|
||||
return nil if list.empty?
|
||||
list
|
||||
end
|
||||
private :addrs2specs
|
||||
|
||||
|
||||
#
|
||||
# date time
|
||||
#
|
||||
|
||||
def date( default = nil )
|
||||
if h = @header['date']
|
||||
h.date
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def date=( time )
|
||||
if time
|
||||
store 'Date', time2str(time)
|
||||
else
|
||||
@header.delete 'date'
|
||||
end
|
||||
time
|
||||
end
|
||||
|
||||
def strftime( fmt, default = nil )
|
||||
if t = date
|
||||
t.strftime(fmt)
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# destination
|
||||
#
|
||||
|
||||
def to_addrs( default = nil )
|
||||
if h = @header['to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def cc_addrs( default = nil )
|
||||
if h = @header['cc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def bcc_addrs( default = nil )
|
||||
if h = @header['bcc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def to_addrs=( arg )
|
||||
set_addrfield 'to', arg
|
||||
end
|
||||
|
||||
def cc_addrs=( arg )
|
||||
set_addrfield 'cc', arg
|
||||
end
|
||||
|
||||
def bcc_addrs=( arg )
|
||||
set_addrfield 'bcc', arg
|
||||
end
|
||||
|
||||
def to( default = nil )
|
||||
addrs2specs(to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def cc( default = nil )
|
||||
addrs2specs(cc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def bcc( default = nil )
|
||||
addrs2specs(bcc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def to=( *strs )
|
||||
set_string_array_attr 'To', strs
|
||||
end
|
||||
|
||||
def cc=( *strs )
|
||||
set_string_array_attr 'Cc', strs
|
||||
end
|
||||
|
||||
def bcc=( *strs )
|
||||
set_string_array_attr 'Bcc', strs
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# originator
|
||||
#
|
||||
|
||||
def from_addrs( default = nil )
|
||||
if h = @header['from']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def from_addrs=( arg )
|
||||
set_addrfield 'from', arg
|
||||
end
|
||||
|
||||
def from( default = nil )
|
||||
addrs2specs(from_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def from=( *strs )
|
||||
set_string_array_attr 'From', strs
|
||||
end
|
||||
|
||||
def friendly_from( default = nil )
|
||||
h = @header['from']
|
||||
a, = h.addrs
|
||||
return default unless a
|
||||
return a.phrase if a.phrase
|
||||
return h.comments.join(' ') unless h.comments.empty?
|
||||
a.spec
|
||||
end
|
||||
|
||||
|
||||
def reply_to_addrs( default = nil )
|
||||
if h = @header['reply-to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def reply_to_addrs=( arg )
|
||||
set_addrfield 'reply-to', arg
|
||||
end
|
||||
|
||||
def reply_to( default = nil )
|
||||
addrs2specs(reply_to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def reply_to=( *strs )
|
||||
set_string_array_attr 'Reply-To', strs
|
||||
end
|
||||
|
||||
|
||||
def sender_addr( default = nil )
|
||||
f = @header['sender'] or return default
|
||||
f.addr or return default
|
||||
end
|
||||
|
||||
def sender_addr=( addr )
|
||||
if addr
|
||||
h = HeaderField.internal_new('sender', @config)
|
||||
h.addr = addr
|
||||
@header['sender'] = h
|
||||
else
|
||||
@header.delete 'sender'
|
||||
end
|
||||
addr
|
||||
end
|
||||
|
||||
def sender( default )
|
||||
f = @header['sender'] or return default
|
||||
a = f.addr or return default
|
||||
a.spec
|
||||
end
|
||||
|
||||
def sender=( str )
|
||||
set_string_attr 'Sender', str
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# subject
|
||||
#
|
||||
|
||||
def subject( default = nil )
|
||||
if h = @header['subject']
|
||||
h.body
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
alias quoted_subject subject
|
||||
|
||||
def subject=( str )
|
||||
set_string_attr 'Subject', str
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# identity & threading
|
||||
#
|
||||
|
||||
def message_id( default = nil )
|
||||
if h = @header['message-id']
|
||||
h.id || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def message_id=( str )
|
||||
set_string_attr 'Message-Id', str
|
||||
end
|
||||
|
||||
def in_reply_to( default = nil )
|
||||
if h = @header['in-reply-to']
|
||||
h.ids
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def in_reply_to=( *idstrs )
|
||||
set_string_array_attr 'In-Reply-To', idstrs
|
||||
end
|
||||
|
||||
def references( default = nil )
|
||||
if h = @header['references']
|
||||
h.refs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def references=( *strs )
|
||||
set_string_array_attr 'References', strs
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# MIME headers
|
||||
#
|
||||
|
||||
def mime_version( default = nil )
|
||||
if h = @header['mime-version']
|
||||
h.version || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def mime_version=( m, opt = nil )
|
||||
if opt
|
||||
if h = @header['mime-version']
|
||||
h.major = m
|
||||
h.minor = opt
|
||||
else
|
||||
store 'Mime-Version', "#{m}.#{opt}"
|
||||
end
|
||||
else
|
||||
store 'Mime-Version', m
|
||||
end
|
||||
m
|
||||
end
|
||||
|
||||
|
||||
def content_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.content_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def main_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.main_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def sub_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.sub_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def set_content_type( str, sub = nil, param = nil )
|
||||
if sub
|
||||
main, sub = str, sub
|
||||
else
|
||||
main, sub = str.split(%r</>, 2)
|
||||
raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
|
||||
end
|
||||
if h = @header['content-type']
|
||||
h.main_type = main
|
||||
h.sub_type = sub
|
||||
h.params.clear
|
||||
else
|
||||
store 'Content-Type', "#{main}/#{sub}"
|
||||
end
|
||||
@header['content-type'].params.replace param if param
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
alias content_type= set_content_type
|
||||
|
||||
def type_param( name, default = nil )
|
||||
if h = @header['content-type']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset( default = nil )
|
||||
if h = @header['content-type']
|
||||
h['charset'] or default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset=( str )
|
||||
if str
|
||||
if h = @header[ 'content-type' ]
|
||||
h['charset'] = str
|
||||
else
|
||||
store 'Content-Type', "text/plain; charset=#{str}"
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
|
||||
def transfer_encoding( default = nil )
|
||||
if h = @header['content-transfer-encoding']
|
||||
h.encoding || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def transfer_encoding=( str )
|
||||
set_string_attr 'Content-Transfer-Encoding', str
|
||||
end
|
||||
|
||||
alias encoding transfer_encoding
|
||||
alias encoding= transfer_encoding=
|
||||
alias content_transfer_encoding transfer_encoding
|
||||
alias content_transfer_encoding= transfer_encoding=
|
||||
|
||||
|
||||
def disposition( default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
alias content_disposition disposition
|
||||
|
||||
def set_disposition( str, params = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition = str
|
||||
h.params.clear
|
||||
else
|
||||
store('Content-Disposition', str)
|
||||
h = @header['content-disposition']
|
||||
end
|
||||
h.params.replace params if params
|
||||
end
|
||||
|
||||
alias disposition= set_disposition
|
||||
alias set_content_disposition set_disposition
|
||||
alias content_disposition= set_disposition
|
||||
|
||||
def disposition_param( name, default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
### utils
|
||||
###
|
||||
|
||||
def create_reply
|
||||
mail = TMail::Mail.parse('')
|
||||
mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
|
||||
mail.to_addrs = reply_addresses([])
|
||||
mail.in_reply_to = [message_id(nil)].compact
|
||||
mail.references = references([]) + [message_id(nil)].compact
|
||||
mail.mime_version = '1.0'
|
||||
mail
|
||||
end
|
||||
|
||||
|
||||
def base64_encode
|
||||
store 'Content-Transfer-Encoding', 'Base64'
|
||||
self.body = Base64.folding_encode(self.body)
|
||||
end
|
||||
|
||||
def base64_decode
|
||||
if /base64/i === self.transfer_encoding('')
|
||||
store 'Content-Transfer-Encoding', '8bit'
|
||||
self.body = Base64.decode(self.body, @config.strict_base64decode?)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def destinations( default = nil )
|
||||
ret = []
|
||||
%w( to cc bcc ).each do |nm|
|
||||
if h = @header[nm]
|
||||
h.addrs.each {|i| ret.push i.address }
|
||||
end
|
||||
end
|
||||
ret.empty? ? default : ret
|
||||
end
|
||||
|
||||
def each_destination( &block )
|
||||
destinations([]).each do |i|
|
||||
if Address === i
|
||||
yield i
|
||||
else
|
||||
i.each(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias each_dest each_destination
|
||||
|
||||
|
||||
def reply_addresses( default = nil )
|
||||
reply_to_addrs(nil) or from_addrs(nil) or default
|
||||
end
|
||||
|
||||
def error_reply_addresses( default = nil )
|
||||
if s = sender(nil)
|
||||
[s]
|
||||
else
|
||||
from_addrs(default)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def multipart?
|
||||
main_type('').downcase == 'multipart'
|
||||
end
|
||||
|
||||
end # class Mail
|
||||
|
||||
end # module TMail
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#
|
||||
# info.rb
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
module TMail
|
||||
|
||||
Version = '0.10.7'
|
||||
Copyright = 'Copyright (c) 1998-2002 Minero Aoki'
|
||||
|
||||
end
|
||||
|
|
@ -1,540 +0,0 @@
|
|||
=begin rdoc
|
||||
|
||||
= Facade.rb Provides an interface to the TMail object
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/utils'
|
||||
|
||||
module TMail
|
||||
|
||||
class Mail
|
||||
|
||||
def header_string( name, default = nil )
|
||||
h = @header[name.downcase] or return default
|
||||
h.to_s
|
||||
end
|
||||
|
||||
###
|
||||
### attributes
|
||||
###
|
||||
|
||||
include TextUtils
|
||||
|
||||
def set_string_array_attr( key, strs )
|
||||
strs.flatten!
|
||||
if strs.empty?
|
||||
@header.delete key.downcase
|
||||
else
|
||||
store key, strs.join(', ')
|
||||
end
|
||||
strs
|
||||
end
|
||||
private :set_string_array_attr
|
||||
|
||||
def set_string_attr( key, str )
|
||||
if str
|
||||
store key, str
|
||||
else
|
||||
@header.delete key.downcase
|
||||
end
|
||||
str
|
||||
end
|
||||
private :set_string_attr
|
||||
|
||||
def set_addrfield( name, arg )
|
||||
if arg
|
||||
h = HeaderField.internal_new(name, @config)
|
||||
h.addrs.replace [arg].flatten
|
||||
@header[name] = h
|
||||
else
|
||||
@header.delete name
|
||||
end
|
||||
arg
|
||||
end
|
||||
private :set_addrfield
|
||||
|
||||
def addrs2specs( addrs )
|
||||
return nil unless addrs
|
||||
list = addrs.map {|addr|
|
||||
if addr.address_group?
|
||||
then addr.map {|a| a.spec }
|
||||
else addr.spec
|
||||
end
|
||||
}.flatten
|
||||
return nil if list.empty?
|
||||
list
|
||||
end
|
||||
private :addrs2specs
|
||||
|
||||
#
|
||||
# date time
|
||||
#
|
||||
|
||||
def date( default = nil )
|
||||
if h = @header['date']
|
||||
h.date
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def date=( time )
|
||||
if time
|
||||
store 'Date', time2str(time)
|
||||
else
|
||||
@header.delete 'date'
|
||||
end
|
||||
time
|
||||
end
|
||||
|
||||
def strftime( fmt, default = nil )
|
||||
if t = date
|
||||
t.strftime(fmt)
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# destination
|
||||
#
|
||||
|
||||
def to_addrs( default = nil )
|
||||
if h = @header['to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def cc_addrs( default = nil )
|
||||
if h = @header['cc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def bcc_addrs( default = nil )
|
||||
if h = @header['bcc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def to_addrs=( arg )
|
||||
set_addrfield 'to', arg
|
||||
end
|
||||
|
||||
def cc_addrs=( arg )
|
||||
set_addrfield 'cc', arg
|
||||
end
|
||||
|
||||
def bcc_addrs=( arg )
|
||||
set_addrfield 'bcc', arg
|
||||
end
|
||||
|
||||
def to( default = nil )
|
||||
addrs2specs(to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def cc( default = nil )
|
||||
addrs2specs(cc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def bcc( default = nil )
|
||||
addrs2specs(bcc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def to=( *strs )
|
||||
set_string_array_attr 'To', strs
|
||||
end
|
||||
|
||||
def cc=( *strs )
|
||||
set_string_array_attr 'Cc', strs
|
||||
end
|
||||
|
||||
def bcc=( *strs )
|
||||
set_string_array_attr 'Bcc', strs
|
||||
end
|
||||
|
||||
#
|
||||
# originator
|
||||
#
|
||||
|
||||
def from_addrs( default = nil )
|
||||
if h = @header['from']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def from_addrs=( arg )
|
||||
set_addrfield 'from', arg
|
||||
end
|
||||
|
||||
def from( default = nil )
|
||||
addrs2specs(from_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def from=( *strs )
|
||||
set_string_array_attr 'From', strs
|
||||
end
|
||||
|
||||
def friendly_from( default = nil )
|
||||
h = @header['from']
|
||||
a, = h.addrs
|
||||
return default unless a
|
||||
return a.phrase if a.phrase
|
||||
return h.comments.join(' ') unless h.comments.empty?
|
||||
a.spec
|
||||
end
|
||||
|
||||
|
||||
def reply_to_addrs( default = nil )
|
||||
if h = @header['reply-to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def reply_to_addrs=( arg )
|
||||
set_addrfield 'reply-to', arg
|
||||
end
|
||||
|
||||
def reply_to( default = nil )
|
||||
addrs2specs(reply_to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def reply_to=( *strs )
|
||||
set_string_array_attr 'Reply-To', strs
|
||||
end
|
||||
|
||||
|
||||
def sender_addr( default = nil )
|
||||
f = @header['sender'] or return default
|
||||
f.addr or return default
|
||||
end
|
||||
|
||||
def sender_addr=( addr )
|
||||
if addr
|
||||
h = HeaderField.internal_new('sender', @config)
|
||||
h.addr = addr
|
||||
@header['sender'] = h
|
||||
else
|
||||
@header.delete 'sender'
|
||||
end
|
||||
addr
|
||||
end
|
||||
|
||||
def sender( default )
|
||||
f = @header['sender'] or return default
|
||||
a = f.addr or return default
|
||||
a.spec
|
||||
end
|
||||
|
||||
def sender=( str )
|
||||
set_string_attr 'Sender', str
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# subject
|
||||
#
|
||||
|
||||
def subject( default = nil )
|
||||
if h = @header['subject']
|
||||
h.body
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
alias quoted_subject subject
|
||||
|
||||
def subject=( str )
|
||||
set_string_attr 'Subject', str
|
||||
end
|
||||
|
||||
#
|
||||
# identity & threading
|
||||
#
|
||||
|
||||
def message_id( default = nil )
|
||||
if h = @header['message-id']
|
||||
h.id || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def message_id=( str )
|
||||
set_string_attr 'Message-Id', str
|
||||
end
|
||||
|
||||
def in_reply_to( default = nil )
|
||||
if h = @header['in-reply-to']
|
||||
h.ids
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def in_reply_to=( *idstrs )
|
||||
set_string_array_attr 'In-Reply-To', idstrs
|
||||
end
|
||||
|
||||
def references( default = nil )
|
||||
if h = @header['references']
|
||||
h.refs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def references=( *strs )
|
||||
set_string_array_attr 'References', strs
|
||||
end
|
||||
|
||||
#
|
||||
# MIME headers
|
||||
#
|
||||
|
||||
def mime_version( default = nil )
|
||||
if h = @header['mime-version']
|
||||
h.version || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def mime_version=( m, opt = nil )
|
||||
if opt
|
||||
if h = @header['mime-version']
|
||||
h.major = m
|
||||
h.minor = opt
|
||||
else
|
||||
store 'Mime-Version', "#{m}.#{opt}"
|
||||
end
|
||||
else
|
||||
store 'Mime-Version', m
|
||||
end
|
||||
m
|
||||
end
|
||||
|
||||
def content_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.content_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def main_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.main_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def sub_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.sub_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def set_content_type( str, sub = nil, param = nil )
|
||||
if sub
|
||||
main, sub = str, sub
|
||||
else
|
||||
main, sub = str.split(%r</>, 2)
|
||||
raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
|
||||
end
|
||||
if h = @header['content-type']
|
||||
h.main_type = main
|
||||
h.sub_type = sub
|
||||
h.params.clear
|
||||
else
|
||||
store 'Content-Type', "#{main}/#{sub}"
|
||||
end
|
||||
@header['content-type'].params.replace param if param
|
||||
str
|
||||
end
|
||||
|
||||
alias content_type= set_content_type
|
||||
|
||||
def type_param( name, default = nil )
|
||||
if h = @header['content-type']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset( default = nil )
|
||||
if h = @header['content-type']
|
||||
h['charset'] or default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset=( str )
|
||||
if str
|
||||
if h = @header[ 'content-type' ]
|
||||
h['charset'] = str
|
||||
else
|
||||
store 'Content-Type', "text/plain; charset=#{str}"
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def transfer_encoding( default = nil )
|
||||
if h = @header['content-transfer-encoding']
|
||||
h.encoding || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def transfer_encoding=( str )
|
||||
set_string_attr 'Content-Transfer-Encoding', str
|
||||
end
|
||||
|
||||
alias encoding transfer_encoding
|
||||
alias encoding= transfer_encoding=
|
||||
alias content_transfer_encoding transfer_encoding
|
||||
alias content_transfer_encoding= transfer_encoding=
|
||||
|
||||
def disposition( default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
alias content_disposition disposition
|
||||
|
||||
def set_disposition( str, params = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition = str
|
||||
h.params.clear
|
||||
else
|
||||
store('Content-Disposition', str)
|
||||
h = @header['content-disposition']
|
||||
end
|
||||
h.params.replace params if params
|
||||
end
|
||||
|
||||
alias disposition= set_disposition
|
||||
alias set_content_disposition set_disposition
|
||||
alias content_disposition= set_disposition
|
||||
|
||||
def disposition_param( name, default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
### utils
|
||||
###
|
||||
|
||||
def create_reply
|
||||
mail = TMail::Mail.parse('')
|
||||
mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
|
||||
mail.to_addrs = reply_addresses([])
|
||||
mail.in_reply_to = [message_id(nil)].compact
|
||||
mail.references = references([]) + [message_id(nil)].compact
|
||||
mail.mime_version = '1.0'
|
||||
mail
|
||||
end
|
||||
|
||||
def base64_encode
|
||||
store 'Content-Transfer-Encoding', 'Base64'
|
||||
self.body = Base64.folding_encode(self.body)
|
||||
end
|
||||
|
||||
def base64_decode
|
||||
if /base64/i === self.transfer_encoding('')
|
||||
store 'Content-Transfer-Encoding', '8bit'
|
||||
self.body = Base64.decode(self.body, @config.strict_base64decode?)
|
||||
end
|
||||
end
|
||||
|
||||
def destinations( default = nil )
|
||||
ret = []
|
||||
%w( to cc bcc ).each do |nm|
|
||||
if h = @header[nm]
|
||||
h.addrs.each {|i| ret.push i.address }
|
||||
end
|
||||
end
|
||||
ret.empty? ? default : ret
|
||||
end
|
||||
|
||||
def each_destination( &block )
|
||||
destinations([]).each do |i|
|
||||
if Address === i
|
||||
yield i
|
||||
else
|
||||
i.each(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias each_dest each_destination
|
||||
|
||||
def reply_addresses( default = nil )
|
||||
reply_to_addrs(nil) or from_addrs(nil) or default
|
||||
end
|
||||
|
||||
def error_reply_addresses( default = nil )
|
||||
if s = sender(nil)
|
||||
[s]
|
||||
else
|
||||
from_addrs(default)
|
||||
end
|
||||
end
|
||||
|
||||
def multipart?
|
||||
main_type('').downcase == 'multipart'
|
||||
end
|
||||
|
||||
end # class Mail
|
||||
|
||||
end # module TMail
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue