mirror of
https://github.com/TracksApp/tracks.git
synced 2025-09-22 05:50:47 +02: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/database.yml
|
||||||
config/environment.rb
|
config/environment.rb
|
||||||
log
|
log
|
||||||
|
@ -5,3 +6,4 @@ tmp
|
||||||
db/data.yml
|
db/data.yml
|
||||||
db/*.sqlite3
|
db/*.sqlite3
|
||||||
nbproject
|
nbproject
|
||||||
|
vendor/plugins/query_trace/
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Likewise will all the methods added be available for all controllers.
|
# Likewise will all the methods added be available for all controllers.
|
||||||
|
|
||||||
require_dependency "login_system"
|
require_dependency "login_system"
|
||||||
require_dependency "source_view"
|
require_dependency "tracks/source_view"
|
||||||
require "redcloth"
|
require "redcloth"
|
||||||
|
|
||||||
require 'date'
|
require 'date'
|
||||||
|
@ -20,6 +20,7 @@ class ApplicationController < ActionController::Base
|
||||||
layout proc{ |controller| controller.mobile? ? "mobile" : "standard" }
|
layout proc{ |controller| controller.mobile? ? "mobile" : "standard" }
|
||||||
|
|
||||||
before_filter :set_session_expiration
|
before_filter :set_session_expiration
|
||||||
|
before_filter :set_time_zone
|
||||||
prepend_before_filter :login_required
|
prepend_before_filter :login_required
|
||||||
prepend_before_filter :enable_mobile_content_negotiation
|
prepend_before_filter :enable_mobile_content_negotiation
|
||||||
after_filter :restore_content_type_for_mobile
|
after_filter :restore_content_type_for_mobile
|
||||||
|
@ -178,6 +179,14 @@ class ApplicationController < ActionController::Base
|
||||||
raise ArgumentError.new("invalid value for Boolean: \"#{s}\"")
|
raise ArgumentError.new("invalid value for Boolean: \"#{s}\"")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.openid_enabled?
|
||||||
|
Tracks::Config.openid_enabled?
|
||||||
|
end
|
||||||
|
|
||||||
|
def openid_enabled?
|
||||||
|
self.class.openid_enabled?
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_date_per_user_prefs( s )
|
def parse_date_per_user_prefs( s )
|
||||||
|
@ -213,4 +222,8 @@ class ApplicationController < ActionController::Base
|
||||||
logger.error("ERROR: #{message}") if type == :error
|
logger.error("ERROR: #{message}") if type == :error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_time_zone
|
||||||
|
Time.zone = current_user.prefs.time_zone if logged_in?
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,11 +6,11 @@ class LoginController < ApplicationController
|
||||||
skip_before_filter :login_required
|
skip_before_filter :login_required
|
||||||
before_filter :login_optional
|
before_filter :login_optional
|
||||||
before_filter :get_current_user
|
before_filter :get_current_user
|
||||||
open_id_consumer if Tracks::Config.openid_enabled?
|
open_id_consumer if openid_enabled?
|
||||||
|
|
||||||
def login
|
def login
|
||||||
@page_title = "TRACKS::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
|
case request.method
|
||||||
when :post
|
when :post
|
||||||
if @user = User.authenticate(params['user_login'], params['user_password'])
|
if @user = User.authenticate(params['user_login'], params['user_password'])
|
||||||
|
|
|
@ -415,7 +415,7 @@ class StatsController < ApplicationController
|
||||||
@max=0
|
@max=0
|
||||||
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
||||||
@actions_creation_hour.each do |r|
|
@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
|
@actions_creation_hour_array[hour] += 1
|
||||||
end
|
end
|
||||||
0.upto(23) { |i| @max = @actions_creation_hour_array[i] if @actions_creation_hour_array[i] > @max}
|
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
|
# convert to hash to be able to fill in non-existing days
|
||||||
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
||||||
@actions_completion_hour.each do |r|
|
@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
|
@actions_completion_hour_array[hour] += 1
|
||||||
end
|
end
|
||||||
0.upto(23) { |i| @max = @actions_completion_hour_array[i] if @actions_completion_hour_array[i] > @max}
|
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
|
@max=0
|
||||||
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
||||||
@actions_creation_hour.each do |r|
|
@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
|
@actions_creation_hour_array[hour] += 1
|
||||||
end
|
end
|
||||||
0.upto(23) { |i| @max = @actions_creation_hour_array[i] if @actions_creation_hour_array[i] > @max}
|
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
|
# convert to hash to be able to fill in non-existing days
|
||||||
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
||||||
@actions_completion_hour.each do |r|
|
@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
|
@actions_completion_hour_array[hour] += 1
|
||||||
end
|
end
|
||||||
0.upto(23) { |i| @max = @actions_completion_hour_array[i] if @actions_completion_hour_array[i] > @max}
|
0.upto(23) { |i| @max = @actions_completion_hour_array[i] if @actions_completion_hour_array[i] > @max}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
|
|
||||||
if Tracks::Config.openid_enabled?
|
if openid_enabled?
|
||||||
open_id_consumer
|
open_id_consumer
|
||||||
before_filter :begin_open_id_auth, :only => :update_auth_type
|
before_filter :begin_open_id_auth, :only => :update_auth_type
|
||||||
end
|
end
|
||||||
|
@ -153,7 +153,7 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_auth_type
|
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
|
case open_id_response.status
|
||||||
when OpenID::SUCCESS
|
when OpenID::SUCCESS
|
||||||
# The URL was a valid identity URL. Now we just need to send a redirect
|
# The URL was a valid identity URL. Now we just need to send a redirect
|
||||||
|
@ -179,7 +179,7 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def complete
|
def complete
|
||||||
return unless Tracks::Config.openid_enabled?
|
return unless openid_enabled?
|
||||||
openid_url = session['openid_url']
|
openid_url = session['openid_url']
|
||||||
if openid_url.blank?
|
if openid_url.blank?
|
||||||
notify :error, "expected an openid_url"
|
notify :error, "expected an openid_url"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
|
|
||||||
def user_time
|
def user_time
|
||||||
current_user.time
|
Time.zone.now
|
||||||
end
|
end
|
||||||
|
|
||||||
# Replicates the link_to method but also checks request.request_uri to find
|
# Replicates the link_to method but also checks request.request_uri to find
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
class Preference < ActiveRecord::Base
|
class Preference < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
composed_of :tz,
|
|
||||||
:class_name => 'TimeZone',
|
|
||||||
:mapping => %w(time_zone name)
|
|
||||||
|
|
||||||
def self.due_styles
|
def self.due_styles
|
||||||
{ :due_in_n_days => 0, :due_on => 1}
|
{ :due_in_n_days => 0, :due_on => 1}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class Project < ActiveRecord::Base
|
class Project < ActiveRecord::Base
|
||||||
has_many :todos, :dependent => :delete_all, :include => :context
|
has_many :todos, :dependent => :delete_all, :include => :context
|
||||||
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
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
|
belongs_to :user
|
||||||
|
|
||||||
validates_presence_of :name, :message => "project must have a name"
|
validates_presence_of :name, :message => "project must have a name"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class Todo < ActiveRecord::Base
|
class Todo < ActiveRecord::Base
|
||||||
|
|
||||||
belongs_to :context, :order => 'name'
|
belongs_to :context
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def time
|
def time
|
||||||
prefs.tz.adjust(Time.now.utc)
|
Time.now.in_time_zone(prefs.time_zone)
|
||||||
end
|
end
|
||||||
|
|
||||||
def date
|
def date
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<li>Last name: <span class="highlight"><%= current_user.last_name %></span></li>
|
<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>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>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>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 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>
|
<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")
|
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME : Ruby 1.9
|
|
||||||
def preinitialize
|
def preinitialize
|
||||||
load(preinitializer_path) if File.exists?(preinitializer_path)
|
load(preinitializer_path) if File.exist?(preinitializer_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def preinitializer_path
|
def preinitializer_path
|
||||||
|
@ -44,6 +43,7 @@ module Rails
|
||||||
class VendorBoot < Boot
|
class VendorBoot < Boot
|
||||||
def load_initializer
|
def load_initializer
|
||||||
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
||||||
|
Rails::Initializer.run(:install_gem_spec_stubs)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,10 @@ Rails::Initializer.run do |config|
|
||||||
# Make Active Record use UTC-base instead of local time
|
# Make Active Record use UTC-base instead of local time
|
||||||
config.active_record.default_timezone = :utc
|
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
|
# 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)
|
# (enables use of different database adapters for development and test environments)
|
||||||
config.active_record.schema_format = :ruby
|
config.active_record.schema_format = :ruby
|
||||||
|
@ -66,18 +70,13 @@ end
|
||||||
|
|
||||||
# Include your application configuration below
|
# 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'].
|
# 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.
|
# If you choose ldap, see the additional configuration options further down.
|
||||||
AUTHENTICATION_SCHEMES = ['database']
|
AUTHENTICATION_SCHEMES = ['database']
|
||||||
|
|
||||||
require 'name_part_finder'
|
require 'name_part_finder'
|
||||||
require 'todo_list'
|
require 'tracks/todo_list'
|
||||||
require 'config'
|
require 'tracks/config'
|
||||||
require 'activerecord_base_tag_extensions' # Needed for tagging-specific extensions
|
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 'digest/sha1' #Needed to support 'rake db:fixtures:load' on some ruby installs: http://dev.rousette.org.uk/ticket/557
|
||||||
require 'prototype_helper_extensions'
|
require 'prototype_helper_extensions'
|
||||||
|
|
|
@ -26,6 +26,8 @@ config.action_controller.allow_forgery_protection = false
|
||||||
# config.pre_loaded_fixtures = false
|
# config.pre_loaded_fixtures = false
|
||||||
SALT = "change-me" unless defined?( SALT ).nil?
|
SALT = "change-me" unless defined?( SALT ).nil?
|
||||||
|
|
||||||
|
config.time_zone = 'UTC'
|
||||||
|
|
||||||
config.after_initialize do
|
config.after_initialize do
|
||||||
require File.expand_path(File.dirname(__FILE__) + "/../../test/selenium_helper")
|
require File.expand_path(File.dirname(__FILE__) + "/../../test/selenium_helper")
|
||||||
end
|
end
|
|
@ -1,3 +1,4 @@
|
||||||
|
<<<<<<< HEAD:config/routes.rb
|
||||||
ActionController::Routing::Routes.draw do |map|
|
ActionController::Routing::Routes.draw do |map|
|
||||||
UJS::routes
|
UJS::routes
|
||||||
|
|
||||||
|
@ -62,3 +63,73 @@ ActionController::Routing::Routes.draw do |map|
|
||||||
map.connect ':controller/:action/:id'
|
map.connect ':controller/:action/:id'
|
||||||
|
|
||||||
end
|
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
|
||||||
|
|
76
db/schema.rb
76
db/schema.rb
|
@ -1,5 +1,5 @@
|
||||||
# This file is auto-generated from the current state of the database. Instead of editing this file,
|
# 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.
|
# then regenerate this schema definition.
|
||||||
#
|
#
|
||||||
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
|
# 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.
|
# 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|
|
create_table "contexts", :force => true do |t|
|
||||||
t.string "name", :null => false
|
t.string "name", :default => "", :null => false
|
||||||
t.integer "position"
|
t.integer "position", :limit => 11
|
||||||
t.boolean "hide", :default => false
|
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 "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
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 => "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|
|
create_table "notes", :force => true do |t|
|
||||||
t.integer "user_id", :null => false
|
t.integer "user_id", :limit => 11, :null => false
|
||||||
t.integer "project_id", :null => false
|
t.integer "project_id", :limit => 11, :null => false
|
||||||
t.text "body"
|
t.text "body"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
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", ["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|
|
create_table "open_id_associations", :force => true do |t|
|
||||||
t.binary "server_url"
|
t.binary "server_url"
|
||||||
t.string "handle"
|
t.string "handle"
|
||||||
t.binary "secret"
|
t.binary "secret"
|
||||||
t.integer "issued"
|
t.integer "issued", :limit => 11
|
||||||
t.integer "lifetime"
|
t.integer "lifetime", :limit => 11
|
||||||
t.string "assoc_type"
|
t.string "assoc_type"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "open_id_nonces", :force => true do |t|
|
create_table "open_id_nonces", :force => true do |t|
|
||||||
t.string "nonce"
|
t.string "nonce"
|
||||||
t.integer "created"
|
t.integer "created", :limit => 11
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "open_id_settings", :force => true do |t|
|
create_table "open_id_settings", :force => true do |t|
|
||||||
|
@ -54,40 +54,40 @@ ActiveRecord::Schema.define(:version => 38) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "preferences", :force => true do |t|
|
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.string "date_format", :limit => 40, :default => "%d/%m/%Y", :null => false
|
||||||
t.integer "week_starts", :default => 0, :null => false
|
t.integer "week_starts", :limit => 11, :default => 0, :null => false
|
||||||
t.integer "show_number_completed", :default => 5, :null => false
|
t.integer "show_number_completed", :limit => 11, :default => 5, :null => false
|
||||||
t.integer "staleness_starts", :default => 7, :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_completed_projects_in_sidebar", :default => true, :null => false
|
||||||
t.boolean "show_hidden_contexts_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.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 "verbose_action_descriptors", :default => false, :null => false
|
||||||
t.boolean "show_hidden_projects_in_sidebar", :default => true, :null => false
|
t.boolean "show_hidden_projects_in_sidebar", :default => true, :null => false
|
||||||
t.string "time_zone", :default => "London", :null => false
|
t.string "time_zone", :default => "London", :null => false
|
||||||
t.boolean "show_project_on_todo_done", :default => false, :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.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
|
end
|
||||||
|
|
||||||
add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id"
|
add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id"
|
||||||
|
|
||||||
create_table "projects", :force => true do |t|
|
create_table "projects", :force => true do |t|
|
||||||
t.string "name", :null => false
|
t.string "name", :default => "", :null => false
|
||||||
t.integer "position"
|
t.integer "position", :limit => 11
|
||||||
t.integer "user_id", :default => 1
|
t.integer "user_id", :limit => 11, :default => 1
|
||||||
t.text "description"
|
t.text "description"
|
||||||
t.string "state", :limit => 20, :default => "active", :null => false
|
t.string "state", :limit => 20, :default => "active", :null => false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "default_context_id"
|
t.integer "default_context_id", :limit => 11
|
||||||
t.datetime "completed_at"
|
t.datetime "completed_at"
|
||||||
end
|
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 => "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|
|
create_table "sessions", :force => true do |t|
|
||||||
t.string "session_id"
|
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"
|
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
|
||||||
|
|
||||||
create_table "taggings", :force => true do |t|
|
create_table "taggings", :force => true do |t|
|
||||||
t.integer "taggable_id"
|
t.integer "taggable_id", :limit => 11
|
||||||
t.integer "tag_id"
|
t.integer "tag_id", :limit => 11
|
||||||
t.string "taggable_type"
|
t.string "taggable_type"
|
||||||
t.integer "user_id"
|
t.integer "user_id", :limit => 11
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "taggings", ["tag_id", "taggable_id", "taggable_type"], :name => "index_taggings_on_tag_id_and_taggable_id_and_taggable_type"
|
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"
|
add_index "tags", ["name"], :name => "index_tags_on_name"
|
||||||
|
|
||||||
create_table "todos", :force => true do |t|
|
create_table "todos", :force => true do |t|
|
||||||
t.integer "context_id", :null => false
|
t.integer "context_id", :limit => 11, :null => false
|
||||||
t.integer "project_id"
|
t.integer "project_id", :limit => 11
|
||||||
t.string "description", :null => false
|
t.string "description", :default => "", :null => false
|
||||||
t.text "notes"
|
t.text "notes"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.date "due"
|
t.date "due"
|
||||||
t.datetime "completed_at"
|
t.datetime "completed_at"
|
||||||
t.integer "user_id", :default => 1
|
t.integer "user_id", :limit => 11, :default => 1
|
||||||
t.date "show_from"
|
t.date "show_from"
|
||||||
t.string "state", :limit => 20, :default => "immediate", :null => false
|
t.string "state", :limit => 20, :default => "immediate", :null => false
|
||||||
end
|
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", "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|
|
create_table "users", :force => true do |t|
|
||||||
t.string "login", :limit => 80, :null => false
|
t.string "login", :limit => 80, :default => "", :null => false
|
||||||
t.string "crypted_password", :limit => 40, :null => false
|
t.string "crypted_password", :limit => 40
|
||||||
t.string "token"
|
t.string "token"
|
||||||
t.boolean "is_admin", :default => false, :null => false
|
t.boolean "is_admin", :default => false, :null => false
|
||||||
t.string "first_name"
|
t.string "first_name"
|
||||||
|
|
|
@ -35,13 +35,14 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<ol>
|
<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>(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>
|
</ol>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<code>
|
<code>
|
||||||
svn co --username=guest
|
cd ~/Sites
|
||||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
git clone git://github.com/bsag/tracks.git
|
||||||
|
cd tracks
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -189,13 +190,15 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<ol>
|
<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>(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>
|
</ol>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<code>
|
<code>
|
||||||
svn co --username=guest \
|
cd ~/Sites
|
||||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
git clone git://github.com/bsag/tracks.git
|
||||||
|
cd tracks
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</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:
|
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).
|
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>
|
<pre>
|
||||||
<code>
|
<code>
|
||||||
svn co --username=guest
|
cd ~/Sites
|
||||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
git clone git://github.com/bsag/tracks.git
|
||||||
|
cd tracks
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</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:
|
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).
|
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>
|
<pre>
|
||||||
<code>
|
<code>
|
||||||
svn co --username=guest \
|
cd ~/Sites
|
||||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
git clone git://github.com/bsag/tracks.git
|
||||||
|
cd tracks
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
|
BIN
doc/manual.pdf
BIN
doc/manual.pdf
Binary file not shown.
|
@ -112,12 +112,12 @@
|
||||||
\usepackage{booktabs} % Better tables
|
\usepackage{booktabs} % Better tables
|
||||||
\usepackage{tabulary} % Support longer table cells
|
\usepackage{tabulary} % Support longer table cells
|
||||||
\usepackage[utf8]{inputenc} % For UTF-8 support
|
\usepackage[utf8]{inputenc} % For UTF-8 support
|
||||||
%\usepackage{xcolor} % Allow for color (annotations)
|
% \usepackage{xcolor} % Allow for color (annotations)
|
||||||
|
%
|
||||||
%\geometry{landscape} % Activate for rotated page geometry
|
% %\geometry{landscape} % Activate for rotated page geometry
|
||||||
|
%
|
||||||
%\usepackage[parfill]{parskip} % Activate to begin paragraphs with an empty
|
% \usepackage[parfill]{parskip} % Activate to begin paragraphs with an empty
|
||||||
% line rather than an indent
|
% % line rather than an indent
|
||||||
|
|
||||||
|
|
||||||
\def\myauthor{Author} % In case these were not included in metadata
|
\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\revision{Revision: \$Id: manual.markdown 864 2008-06-03 17:01:00Z bsag \$}
|
||||||
\def\mytitle{Tracks 1.6 Manual}
|
\def\mytitle{Tracks 1.6 Manual}
|
||||||
\def\version{1.6}
|
\def\version{1.6}
|
||||||
%%\usepackage{xmpincl}
|
% \usepackage{xmpincl}
|
||||||
%%\includexmp{CCAttributionShareAlike}
|
% \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 (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}
|
\end{enumerate}
|
||||||
|
|
||||||
\begin{adjustwidth}{2.5em}{2.5em}
|
\begin{adjustwidth}{2.5em}{2.5em}
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
|
|
||||||
|
|
||||||
svn co --username=guest
|
cd ~/Sites
|
||||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
git clone git://github.com/bsag/tracks.git
|
||||||
|
cd tracks
|
||||||
|
|
||||||
|
|
||||||
\end{verbatim}
|
\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 (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}
|
\end{enumerate}
|
||||||
|
|
||||||
\begin{adjustwidth}{2.5em}{2.5em}
|
\begin{adjustwidth}{2.5em}{2.5em}
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
|
|
||||||
|
|
||||||
svn co --username=guest \
|
cd ~/Sites
|
||||||
http://www.rousette.org.uk/svn/tracks-repos/tags/current tracks
|
git clone git://github.com/bsag/tracks.git
|
||||||
|
cd tracks
|
||||||
|
|
||||||
|
|
||||||
\end{verbatim}
|
\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 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
||||||
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
|
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
|
||||||
// Contributors:
|
// 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)
|
// (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.
|
// 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:
|
// Contributors:
|
||||||
// Justin Palmer (http://encytemedia.com/)
|
// Justin Palmer (http://encytemedia.com/)
|
||||||
// Mark Pilgrim (http://diveintomark.org/)
|
// 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
|
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||||
<%
|
<%
|
||||||
def today
|
def today
|
||||||
Time.now.utc.beginning_of_day.to_s(:db)
|
Time.zone.now.beginning_of_day.to_s(:db)
|
||||||
end
|
end
|
||||||
%>
|
%>
|
||||||
|
|
||||||
|
|
10
test/fixtures/todos.yml
vendored
10
test/fixtures/todos.yml
vendored
|
@ -2,23 +2,23 @@
|
||||||
<%
|
<%
|
||||||
|
|
||||||
def today
|
def today
|
||||||
Time.now.utc.beginning_of_day.to_s(:db)
|
Time.zone.now.beginning_of_day.to_s(:db)
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_week
|
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
|
end
|
||||||
|
|
||||||
def last_week
|
def last_week
|
||||||
1.week.ago.utc.beginning_of_day.to_s(:db)
|
1.week.ago.beginning_of_day.to_s(:db)
|
||||||
end
|
end
|
||||||
|
|
||||||
def two_weeks_ago
|
def two_weeks_ago
|
||||||
2.weeks.ago.utc.beginning_of_day.to_s(:db)
|
2.weeks.ago.beginning_of_day.to_s(:db)
|
||||||
end
|
end
|
||||||
|
|
||||||
def two_weeks_hence
|
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
|
end
|
||||||
|
|
||||||
%>
|
%>
|
||||||
|
|
|
@ -103,7 +103,7 @@ class ContextsControllerTest < TodoContainerControllerTestBase
|
||||||
assert_select 'p', /\d+ actions. Context is (Active|Hidden)./
|
assert_select 'p', /\d+ actions. Context is (Active|Hidden)./
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -160,7 +160,7 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
|
||||||
assert_select 'p', /\d+ actions. Project is (active|hidden|completed)./
|
assert_select 'p', /\d+ actions. Project is (active|hidden|completed)./
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,360 +1,360 @@
|
||||||
require File.dirname(__FILE__) + '/../test_helper'
|
require File.dirname(__FILE__) + '/../test_helper'
|
||||||
require 'todos_controller'
|
require 'todos_controller'
|
||||||
|
|
||||||
# Re-raise errors caught by the controller.
|
# Re-raise errors caught by the controller.
|
||||||
class TodosController; def rescue_action(e) raise e end; end
|
class TodosController; def rescue_action(e) raise e end; end
|
||||||
|
|
||||||
class TodosControllerTest < Test::Rails::TestCase
|
class TodosControllerTest < Test::Rails::TestCase
|
||||||
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings
|
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@controller = TodosController.new
|
@controller = TodosController.new
|
||||||
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_get_index_when_not_logged_in
|
def test_get_index_when_not_logged_in
|
||||||
get :index
|
get :index
|
||||||
assert_redirected_to :controller => 'login', :action => 'login'
|
assert_redirected_to :controller => 'login', :action => 'login'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_not_done_counts
|
def test_not_done_counts
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index
|
get :index
|
||||||
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
|
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
|
||||||
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
|
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
|
||||||
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
|
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_tag_is_retrieved_properly
|
def test_tag_is_retrieved_properly
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index
|
get :index
|
||||||
t = assigns['not_done_todos'].find{|t| t.id == 2}
|
t = assigns['not_done_todos'].find{|t| t.id == 2}
|
||||||
assert_equal 1, t.tags.count
|
assert_equal 1, t.tags.count
|
||||||
assert_equal 'foo', t.tags[0].name
|
assert_equal 'foo', t.tags[0].name
|
||||||
assert !t.starred?
|
assert !t.starred?
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_not_done_counts_after_hiding_project
|
def test_not_done_counts_after_hiding_project
|
||||||
p = Project.find(1)
|
p = Project.find(1)
|
||||||
p.hide!
|
p.hide!
|
||||||
p.save!
|
p.save!
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index
|
get :index
|
||||||
assert_equal nil, assigns['project_not_done_counts'][projects(:timemachine).id]
|
assert_equal nil, assigns['project_not_done_counts'][projects(:timemachine).id]
|
||||||
assert_equal 2, assigns['context_not_done_counts'][contexts(:call).id]
|
assert_equal 2, assigns['context_not_done_counts'][contexts(:call).id]
|
||||||
assert_equal nil, assigns['context_not_done_counts'][contexts(:lab).id]
|
assert_equal nil, assigns['context_not_done_counts'][contexts(:lab).id]
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_not_done_counts_after_hiding_and_unhiding_project
|
def test_not_done_counts_after_hiding_and_unhiding_project
|
||||||
p = Project.find(1)
|
p = Project.find(1)
|
||||||
p.hide!
|
p.hide!
|
||||||
p.save!
|
p.save!
|
||||||
p.activate!
|
p.activate!
|
||||||
p.save!
|
p.save!
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index
|
get :index
|
||||||
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
|
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
|
||||||
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
|
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
|
||||||
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
|
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_deferred_count_for_project_source_view
|
def test_deferred_count_for_project_source_view
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
xhr :post, :toggle_check, :id => 5, :_source_view => 'project'
|
xhr :post, :toggle_check, :id => 5, :_source_view => 'project'
|
||||||
assert_equal 1, assigns['deferred_count']
|
assert_equal 1, assigns['deferred_count']
|
||||||
xhr :post, :toggle_check, :id => 15, :_source_view => 'project'
|
xhr :post, :toggle_check, :id => 15, :_source_view => 'project'
|
||||||
assert_equal 0, assigns['deferred_count']
|
assert_equal 0, assigns['deferred_count']
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_destroy_todo
|
def test_destroy_todo
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
xhr :post, :destroy, :id => 1, :_source_view => 'todo'
|
xhr :post, :destroy, :id => 1, :_source_view => 'todo'
|
||||||
assert_rjs :page, "todo_1", :remove
|
assert_rjs :page, "todo_1", :remove
|
||||||
#assert_rjs :replace_html, "badge-count", '9'
|
#assert_rjs :replace_html, "badge-count", '9'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_create_todo
|
def test_create_todo
|
||||||
assert_difference Todo, :count do
|
assert_difference Todo, :count do
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
|
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_create_todo_via_xml
|
def test_create_todo_via_xml
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
assert_difference Todo, :count do
|
assert_difference Todo, :count do
|
||||||
put :create, :format => "xml", "request" => { "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
|
put :create, :format => "xml", "request" => { "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
|
||||||
assert_response 201
|
assert_response 201
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fail_to_create_todo_via_xml
|
def test_fail_to_create_todo_via_xml
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
#try to create with no context, which is not valid
|
#try to create with no context, which is not valid
|
||||||
put :create, :format => "xml", "request" => { "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
|
put :create, :format => "xml", "request" => { "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
|
||||||
assert_response 422
|
assert_response 422
|
||||||
assert_xml_select "errors" do
|
assert_xml_select "errors" do
|
||||||
assert_xml_select "error", "Context can't be blank"
|
assert_xml_select "error", "Context can't be blank"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_create_deferred_todo
|
def test_create_deferred_todo
|
||||||
original_todo_count = Todo.count
|
original_todo_count = Todo.count
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2026", 'show_from' => '30/10/2026'}, "tag_list"=>"foo bar"
|
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2026", 'show_from' => '30/10/2026'}, "tag_list"=>"foo bar"
|
||||||
assert_equal original_todo_count + 1, Todo.count
|
assert_equal original_todo_count + 1, Todo.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_update_todo_project
|
def test_update_todo_project
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
|
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
assert_equal 1, t.project_id
|
assert_equal 1, t.project_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_update_todo_project_to_none
|
def test_update_todo_project_to_none
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"None", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
|
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"None", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
assert_nil t.project_id
|
assert_nil t.project_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_update_todo_to_deferred_is_reflected_in_badge_count
|
def test_update_todo_to_deferred_is_reflected_in_badge_count
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index
|
get :index
|
||||||
assert_equal 10, assigns['count']
|
assert_equal 10, assigns['count']
|
||||||
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Make more money than Billy Gates", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006", "show_from"=>"30/11/2030"}, "tag_list"=>"foo bar"
|
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Make more money than Billy Gates", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006", "show_from"=>"30/11/2030"}, "tag_list"=>"foo bar"
|
||||||
assert_equal 9, assigns['down_count']
|
assert_equal 9, assigns['down_count']
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_update_todo
|
def test_update_todo
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
|
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
assert_equal "Call Warren Buffet to find out how much he makes per day", t.description
|
assert_equal "Call Warren Buffet to find out how much he makes per day", t.description
|
||||||
assert_equal "foo, bar", t.tag_list
|
assert_equal "foo, bar", t.tag_list
|
||||||
expected = Date.new(2006,11,30)
|
expected = Date.new(2006,11,30)
|
||||||
actual = t.due
|
actual = t.due
|
||||||
assert_equal expected, actual, "Expected #{expected.to_s(:db)}, was #{actual.to_s(:db)}"
|
assert_equal expected, actual, "Expected #{expected.to_s(:db)}, was #{actual.to_s(:db)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_update_todos_with_blank_project_name
|
def test_update_todos_with_blank_project_name
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
xhr :post, :update, :id => 1, :_source_view => 'todo', :project_name => '', "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
|
xhr :post, :update, :id => 1, :_source_view => 'todo', :project_name => '', "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
|
||||||
t.reload
|
t.reload
|
||||||
assert t.project.nil?
|
assert t.project.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_update_todo_tags_to_none
|
def test_update_todo_tags_to_none
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>""
|
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>""
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
assert_equal true, t.tag_list.empty?
|
assert_equal true, t.tag_list.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_update_todo_tags_with_whitespace_and_dots
|
def test_update_todo_tags_with_whitespace_and_dots
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
taglist = " one , two,three ,four, 8.1.2, version1.5"
|
taglist = " one , two,three ,four, 8.1.2, version1.5"
|
||||||
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>taglist
|
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>taglist
|
||||||
t = Todo.find(1)
|
t = Todo.find(1)
|
||||||
assert_equal "one, two, three, four, 8.1.2, version1.5", t.tag_list
|
assert_equal "one, two, three, four, 8.1.2, version1.5", t.tag_list
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_find_tagged_with
|
def test_find_tagged_with
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
@user = User.find(@request.session['user_id'])
|
@user = User.find(@request.session['user_id'])
|
||||||
tag = Tag.find_by_name('foo').todos
|
tag = Tag.find_by_name('foo').todos
|
||||||
@tagged = tag.find(:all, :conditions => ['taggings.user_id = ?', @user.id]).size
|
@tagged = tag.find(:all, :conditions => ['taggings.user_id = ?', @user.id]).size
|
||||||
get :tag, :name => 'foo'
|
get :tag, :name => 'foo'
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_equal 3, @tagged
|
assert_equal 3, @tagged
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_rss_feed
|
def test_rss_feed
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index, { :format => "rss" }
|
get :index, { :format => "rss" }
|
||||||
assert_equal 'application/rss+xml', @response.content_type
|
assert_equal 'application/rss+xml', @response.content_type
|
||||||
#puts @response.body
|
#puts @response.body
|
||||||
|
|
||||||
assert_xml_select 'rss[version="2.0"]' do
|
assert_xml_select 'rss[version="2.0"]' do
|
||||||
assert_select 'channel' do
|
assert_select 'channel' do
|
||||||
assert_select '>title', 'Tracks Actions'
|
assert_select '>title', 'Tracks Actions'
|
||||||
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
||||||
assert_select 'language', 'en-us'
|
assert_select 'language', 'en-us'
|
||||||
assert_select 'ttl', '40'
|
assert_select 'ttl', '40'
|
||||||
assert_select 'item', 10 do
|
assert_select 'item', 10 do
|
||||||
assert_select 'title', /.+/
|
assert_select 'title', /.+/
|
||||||
assert_select 'description', /.*/
|
assert_select 'description', /.*/
|
||||||
assert_select 'link', %r{http://test.host/contexts/.+}
|
assert_select 'link', %r{http://test.host/contexts/.+}
|
||||||
assert_select 'guid', %r{http://test.host/todos/.+}
|
assert_select 'guid', %r{http://test.host/todos/.+}
|
||||||
assert_select 'pubDate', projects(:timemachine).updated_at.to_s(:rfc822)
|
assert_select 'pubDate', projects(:timemachine).updated_at.to_s(:rfc822)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_rss_feed_with_limit
|
def test_rss_feed_with_limit
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index, { :format => "rss", :limit => '5' }
|
get :index, { :format => "rss", :limit => '5' }
|
||||||
|
|
||||||
assert_xml_select 'rss[version="2.0"]' do
|
assert_xml_select 'rss[version="2.0"]' do
|
||||||
assert_select 'channel' do
|
assert_select 'channel' do
|
||||||
assert_select '>title', 'Tracks Actions'
|
assert_select '>title', 'Tracks Actions'
|
||||||
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
||||||
assert_select 'item', 5 do
|
assert_select 'item', 5 do
|
||||||
assert_select 'title', /.+/
|
assert_select 'title', /.+/
|
||||||
assert_select 'description', /.*/
|
assert_select 'description', /.*/
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_rss_feed_not_accessible_to_anonymous_user_without_token
|
def test_rss_feed_not_accessible_to_anonymous_user_without_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "rss" }
|
get :index, { :format => "rss" }
|
||||||
assert_response 401
|
assert_response 401
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
|
def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "rss", :token => 'foo' }
|
get :index, { :format => "rss", :token => 'foo' }
|
||||||
assert_response 401
|
assert_response 401
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_rss_feed_accessible_to_anonymous_user_with_valid_token
|
def test_rss_feed_accessible_to_anonymous_user_with_valid_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "rss", :token => users(:admin_user).token }
|
get :index, { :format => "rss", :token => users(:admin_user).token }
|
||||||
assert_response :ok
|
assert_response :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_atom_feed_content
|
def test_atom_feed_content
|
||||||
login_as :admin_user
|
login_as :admin_user
|
||||||
get :index, { :format => "atom" }
|
get :index, { :format => "atom" }
|
||||||
assert_equal 'application/atom+xml', @response.content_type
|
assert_equal 'application/atom+xml', @response.content_type
|
||||||
#puts @response.body
|
#puts @response.body
|
||||||
|
|
||||||
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
|
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
|
||||||
assert_xml_select '>title', 'Tracks Actions'
|
assert_xml_select '>title', 'Tracks Actions'
|
||||||
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
|
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
|
||||||
assert_xml_select 'entry', 10 do
|
assert_xml_select 'entry', 10 do
|
||||||
assert_xml_select 'title', /.+/
|
assert_xml_select 'title', /.+/
|
||||||
assert_xml_select 'content[type="html"]', /.*/
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_atom_feed_not_accessible_to_anonymous_user_without_token
|
def test_atom_feed_not_accessible_to_anonymous_user_without_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "atom" }
|
get :index, { :format => "atom" }
|
||||||
assert_response 401
|
assert_response 401
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
|
def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "atom", :token => 'foo' }
|
get :index, { :format => "atom", :token => 'foo' }
|
||||||
assert_response 401
|
assert_response 401
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_atom_feed_accessible_to_anonymous_user_with_valid_token
|
def test_atom_feed_accessible_to_anonymous_user_with_valid_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "atom", :token => users(:admin_user).token }
|
get :index, { :format => "atom", :token => users(:admin_user).token }
|
||||||
assert_response :ok
|
assert_response :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_text_feed_content
|
def test_text_feed_content
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index, { :format => "txt" }
|
get :index, { :format => "txt" }
|
||||||
assert_equal 'text/plain', @response.content_type
|
assert_equal 'text/plain', @response.content_type
|
||||||
assert !(/ /.match(@response.body))
|
assert !(/ /.match(@response.body))
|
||||||
#puts @response.body
|
#puts @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_text_feed_not_accessible_to_anonymous_user_without_token
|
def test_text_feed_not_accessible_to_anonymous_user_without_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "txt" }
|
get :index, { :format => "txt" }
|
||||||
assert_response 401
|
assert_response 401
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
|
def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "txt", :token => 'foo' }
|
get :index, { :format => "txt", :token => 'foo' }
|
||||||
assert_response 401
|
assert_response 401
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_text_feed_accessible_to_anonymous_user_with_valid_token
|
def test_text_feed_accessible_to_anonymous_user_with_valid_token
|
||||||
login_as nil
|
login_as nil
|
||||||
get :index, { :format => "txt", :token => users(:admin_user).token }
|
get :index, { :format => "txt", :token => users(:admin_user).token }
|
||||||
assert_response :ok
|
assert_response :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_ical_feed_content
|
def test_ical_feed_content
|
||||||
login_as :admin_user
|
login_as :admin_user
|
||||||
get :index, { :format => "ics" }
|
get :index, { :format => "ics" }
|
||||||
assert_equal 'text/calendar', @response.content_type
|
assert_equal 'text/calendar', @response.content_type
|
||||||
assert !(/ /.match(@response.body))
|
assert !(/ /.match(@response.body))
|
||||||
#puts @response.body
|
#puts @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_mobile_index_uses_text_html_content_type
|
def test_mobile_index_uses_text_html_content_type
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index, { :format => "m" }
|
get :index, { :format => "m" }
|
||||||
assert_equal 'text/html', @response.content_type
|
assert_equal 'text/html', @response.content_type
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_mobile_index_assigns_down_count
|
def test_mobile_index_assigns_down_count
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index, { :format => "m" }
|
get :index, { :format => "m" }
|
||||||
assert_equal 10, assigns['down_count']
|
assert_equal 10, assigns['down_count']
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_mobile_create_action_creates_a_new_todo
|
def test_mobile_create_action_creates_a_new_todo
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
|
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
|
||||||
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
||||||
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
||||||
"project_id"=>"1",
|
"project_id"=>"1",
|
||||||
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
|
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
|
||||||
t = Todo.find_by_description("test_mobile_create_action")
|
t = Todo.find_by_description("test_mobile_create_action")
|
||||||
assert_not_nil t
|
assert_not_nil t
|
||||||
assert_equal 2, t.context_id
|
assert_equal 2, t.context_id
|
||||||
assert_equal 1, t.project_id
|
assert_equal 1, t.project_id
|
||||||
assert t.active?
|
assert t.active?
|
||||||
assert_equal 'test notes', t.notes
|
assert_equal 'test notes', t.notes
|
||||||
assert_nil t.show_from
|
assert_nil t.show_from
|
||||||
assert_equal Date.new(2007,1,2).to_s, t.due.to_s
|
assert_equal Date.new(2007,1,2).to_s, t.due.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_mobile_create_action_redirects_to_mobile_home_page_when_successful
|
def test_mobile_create_action_redirects_to_mobile_home_page_when_successful
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
|
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
|
||||||
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
||||||
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
||||||
"project_id"=>"1",
|
"project_id"=>"1",
|
||||||
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
|
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
|
||||||
assert_redirected_to '/m'
|
assert_redirected_to '/m'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_mobile_create_action_renders_new_template_when_save_fails
|
def test_mobile_create_action_renders_new_template_when_save_fails
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
|
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
|
||||||
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
|
||||||
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
|
||||||
"project_id"=>"1",
|
"project_id"=>"1",
|
||||||
"notes"=>"test notes", "state"=>"0"}, "tag_list"=>"test, test2"}
|
"notes"=>"test notes", "state"=>"0"}, "tag_list"=>"test, test2"}
|
||||||
assert_template 'todos/new'
|
assert_template 'todos/new'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_index_html_assigns_default_project_name_map
|
def test_index_html_assigns_default_project_name_map
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index, {"format"=>"html"}
|
get :index, {"format"=>"html"}
|
||||||
assert_equal '"{\\"Build a working time machine\\": \\"lab\\"}"', assigns(:default_project_context_name_map)
|
assert_equal '"{\\"Build a working time machine\\": \\"lab\\"}"', assigns(:default_project_context_name_map)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ require 'contexts_controller'
|
||||||
class ContextsController; def rescue_action(e) raise e end; end
|
class ContextsController; def rescue_action(e) raise e end; end
|
||||||
|
|
||||||
class ContextXmlApiTest < ActionController::IntegrationTest
|
class ContextXmlApiTest < ActionController::IntegrationTest
|
||||||
fixtures :users, :contexts
|
fixtures :users, :contexts, :preferences
|
||||||
|
|
||||||
@@context_name = "@newcontext"
|
@@context_name = "@newcontext"
|
||||||
@@valid_postdata = "<request><context><name>#{@@context_name}</name></context></request>"
|
@@valid_postdata = "<request><context><name>#{@@context_name}</name></context></request>"
|
||||||
|
|
|
@ -12,8 +12,6 @@ class SeleniumHelperController < ActionController::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ActionController::Routing::Routes.add_route '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login'
|
|
||||||
|
|
||||||
module SeleniumOnRails::TestBuilderActions
|
module SeleniumOnRails::TestBuilderActions
|
||||||
def login options = {}
|
def login options = {}
|
||||||
options = {options => nil} unless options.is_a? Hash
|
options = {options => nil} unless options.is_a? Hash
|
||||||
|
|
|
@ -12,7 +12,6 @@ class PreferenceTest < Test::Rails::TestCase
|
||||||
|
|
||||||
def test_time_zone
|
def test_time_zone
|
||||||
assert_equal 'London', @admin_user.preference.time_zone
|
assert_equal 'London', @admin_user.preference.time_zone
|
||||||
assert_equal @admin_user.preference.tz, TimeZone['London']
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_show_project_on_todo_done
|
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_equal "Call Bill Gates to find out how much he makes per day", @not_completed1.description
|
||||||
assert_nil @not_completed1.notes
|
assert_nil @not_completed1.notes
|
||||||
assert @not_completed1.completed? == false
|
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 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.utc.beginning_of_day.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
|
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_nil @not_completed1.completed_at
|
||||||
assert_equal 1, @not_completed1.user_id
|
assert_equal 1, @not_completed1.user_id
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,10 +10,6 @@ class TodosHelperTest < Test::Rails::HelperTestCase
|
||||||
include ApplicationHelper
|
include ApplicationHelper
|
||||||
include TodosHelper
|
include TodosHelper
|
||||||
|
|
||||||
def user_time
|
|
||||||
Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_date(date)
|
def format_date(date)
|
||||||
if date
|
if date
|
||||||
date_format = "%d/%m/%Y"
|
date_format = "%d/%m/%Y"
|
||||||
|
@ -31,7 +27,7 @@ class TodosHelperTest < Test::Rails::HelperTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_show_date_today
|
def test_show_date_today
|
||||||
date = Time.now.to_date
|
date = Time.zone.now.to_date
|
||||||
html = show_date(date)
|
html = show_date(date)
|
||||||
formatted_date = format_date(date)
|
formatted_date = format_date(date)
|
||||||
assert_equal %Q{<a title="#{formatted_date}"><span class="amber">Show Today</span></a> }, html
|
assert_equal %Q{<a title="#{formatted_date}"><span class="amber">Show Today</span></a> }, html
|
||||||
|
|
40
vendor/plugins/selenium-on-rails/init.rb
vendored
40
vendor/plugins/selenium-on-rails/init.rb
vendored
|
@ -1,19 +1,21 @@
|
||||||
require 'selenium_on_rails_config'
|
require 'selenium_on_rails_config'
|
||||||
envs = SeleniumOnRailsConfig.get :environments
|
envs = SeleniumOnRailsConfig.get :environments
|
||||||
|
|
||||||
if envs.include? RAILS_ENV
|
if envs.include? RAILS_ENV
|
||||||
#initialize the plugin
|
#initialize the plugin
|
||||||
$LOAD_PATH << File.dirname(__FILE__) + "/lib/controllers"
|
$LOAD_PATH << File.dirname(__FILE__) + "/lib/controllers"
|
||||||
require 'selenium_controller'
|
require 'selenium_controller'
|
||||||
require File.dirname(__FILE__) + '/routes'
|
require File.dirname(__FILE__) + '/routes'
|
||||||
|
|
||||||
else
|
SeleniumController.prepend_view_path File.expand_path(File.dirname(__FILE__) + '/lib/views')
|
||||||
#erase all traces
|
|
||||||
$LOAD_PATH.delete lib_path
|
else
|
||||||
|
#erase all traces
|
||||||
#but help user figure out what to do
|
$LOAD_PATH.delete lib_path
|
||||||
unless RAILS_ENV == 'production' # don't pollute production
|
|
||||||
require File.dirname(__FILE__) + '/switch_environment/init'
|
#but help user figure out what to do
|
||||||
end
|
unless RAILS_ENV == 'production' # don't pollute production
|
||||||
end
|
require File.dirname(__FILE__) + '/switch_environment/init'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,61 +1,56 @@
|
||||||
module SeleniumOnRails
|
module SeleniumOnRails
|
||||||
module Paths
|
module Paths
|
||||||
def selenium_path
|
def selenium_path
|
||||||
@@selenium_path ||= find_selenium_path
|
@@selenium_path ||= find_selenium_path
|
||||||
@@selenium_path
|
@@selenium_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def selenium_tests_path
|
def selenium_tests_path
|
||||||
File.expand_path(File.join(RAILS_ROOT, 'test/selenium'))
|
File.expand_path(File.join(RAILS_ROOT, 'test/selenium'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def view_path view
|
def view_path view
|
||||||
File.expand_path(File.dirname(__FILE__) + '/../views/' + view)
|
File.expand_path(File.dirname(__FILE__) + '/../views/' + view)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the path to the layout template. The path is relative in relation
|
def layout_path
|
||||||
# to the app/views/ directory since Rails doesn't support absolute paths
|
'/layout.rhtml'
|
||||||
# to layout templates.
|
end
|
||||||
def layout_path
|
|
||||||
rails_root = Pathname.new File.expand_path(File.join(RAILS_ROOT, 'app/views'))
|
def fixtures_path
|
||||||
view_path = Pathname.new view_path('layout')
|
File.expand_path File.join(RAILS_ROOT, 'test/fixtures')
|
||||||
view_path.relative_path_from(rails_root).to_s
|
end
|
||||||
end
|
|
||||||
|
def log_path log_file
|
||||||
def fixtures_path
|
File.expand_path(File.dirname(__FILE__) + '/../../log/' + File.basename(log_file))
|
||||||
File.expand_path File.join(RAILS_ROOT, 'test/fixtures')
|
end
|
||||||
end
|
|
||||||
|
def skip_file? file
|
||||||
def log_path log_file
|
file.split('/').each do |f|
|
||||||
File.expand_path(File.dirname(__FILE__) + '/../../log/' + File.basename(log_file))
|
return true if f.upcase == 'CVS' or f.starts_with?('.') or f.ends_with?('~') or f.starts_with?('_')
|
||||||
end
|
end
|
||||||
|
false
|
||||||
def skip_file? file
|
end
|
||||||
file.split('/').each do |f|
|
|
||||||
return true if f.upcase == 'CVS' or f.starts_with?('.') or f.ends_with?('~') or f.starts_with?('_')
|
private
|
||||||
end
|
def find_selenium_path
|
||||||
false
|
sel_dirs = SeleniumOnRailsConfig.get :selenium_path do
|
||||||
end
|
ds = [File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium')),
|
||||||
|
File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium-core'))]
|
||||||
private
|
gems = Gem.source_index.find_name 'selenium', nil
|
||||||
def find_selenium_path
|
ds << gems.last.full_gem_path unless gems.empty?
|
||||||
sel_dirs = SeleniumOnRailsConfig.get :selenium_path do
|
ds
|
||||||
ds = [File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium')),
|
end
|
||||||
File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium-core'))]
|
|
||||||
gems = Gem.source_index.find_name 'selenium', nil
|
sel_dirs.to_a.each do |seleniumdir|
|
||||||
ds << gems.last.full_gem_path unless gems.empty?
|
['', 'core', 'selenium', 'javascript'].each do |subdir|
|
||||||
ds
|
path = File.join seleniumdir, subdir
|
||||||
end
|
return path if File.exist?(File.join(path, 'TestRunner.html'))
|
||||||
|
end
|
||||||
sel_dirs.to_a.each do |seleniumdir|
|
end
|
||||||
['', 'core', 'selenium', 'javascript'].each do |subdir|
|
|
||||||
path = File.join seleniumdir, subdir
|
raise 'Could not find Selenium Core installation'
|
||||||
return path if File.exist?(File.join(path, 'TestRunner.html'))
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
raise 'Could not find Selenium Core installation'
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
# Renders Selenium test templates in a fashion analogous to +rxml+ and
|
# Renders Selenium test templates in a fashion analogous to +rxml+ and
|
||||||
# +rjs+ templates.
|
# +rjs+ templates.
|
||||||
#
|
#
|
||||||
# setup
|
# setup
|
||||||
# open :controller => 'customer', :action => 'list'
|
# open :controller => 'customer', :action => 'list'
|
||||||
# assert_title 'Customers'
|
# assert_title 'Customers'
|
||||||
#
|
#
|
||||||
# See SeleniumOnRails::TestBuilder for a list of available commands.
|
# See SeleniumOnRails::TestBuilder for a list of available commands.
|
||||||
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
||||||
end
|
end
|
||||||
ActionView::Base.register_template_handler 'rsel', SeleniumOnRails::RSelenese
|
ActionView::Template.register_template_handler 'rsel', SeleniumOnRails::RSelenese
|
||||||
|
|
||||||
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
||||||
attr_accessor :view
|
attr_accessor :view
|
||||||
|
|
||||||
# Create a new RSelenese renderer bound to _view_.
|
# Create a new RSelenese renderer bound to _view_.
|
||||||
def initialize view
|
def initialize view
|
||||||
super view
|
super view
|
||||||
@view = view
|
@view = view
|
||||||
end
|
end
|
||||||
|
|
||||||
# Render _template_ using _local_assigns_.
|
# Render _template_ using _local_assigns_.
|
||||||
def render template, local_assigns
|
def render template
|
||||||
title = (@view.assigns['page_title'] or local_assigns['page_title'])
|
title = @view.assigns['page_title']
|
||||||
table(title) do
|
table(title) do
|
||||||
test = self #to enable test.command
|
test = self #to enable test.command
|
||||||
|
eval template.source
|
||||||
assign_locals_code = ''
|
end
|
||||||
local_assigns.each_key {|key| assign_locals_code << "#{key} = local_assigns[#{key.inspect}];"}
|
end
|
||||||
|
|
||||||
eval assign_locals_code + "\n" + template
|
def compilable?
|
||||||
end
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -1,81 +1,85 @@
|
||||||
class SeleniumOnRails::Selenese
|
class SeleniumOnRails::Selenese
|
||||||
end
|
end
|
||||||
ActionView::Base.register_template_handler 'sel', SeleniumOnRails::Selenese
|
ActionView::Template.register_template_handler 'sel', SeleniumOnRails::Selenese
|
||||||
|
|
||||||
|
|
||||||
class SeleniumOnRails::Selenese
|
class SeleniumOnRails::Selenese
|
||||||
def initialize view
|
def initialize view
|
||||||
@view = view
|
@view = view
|
||||||
end
|
end
|
||||||
|
|
||||||
def render template, local_assigns
|
def render template
|
||||||
name = (@view.assigns['page_title'] or local_assigns['page_title'])
|
name = @view.assigns['page_title']
|
||||||
lines = template.strip.split "\n"
|
lines = template.strip.split "\n"
|
||||||
html = ''
|
html = ''
|
||||||
html << extract_comments(lines)
|
html << extract_comments(lines)
|
||||||
html << extract_commands(lines, name)
|
html << extract_commands(lines, name)
|
||||||
html << extract_comments(lines)
|
html << extract_comments(lines)
|
||||||
raise 'You cannot have comments in the middle of commands!' if next_line lines, :any
|
raise 'You cannot have comments in the middle of commands!' if next_line lines, :any
|
||||||
html
|
html
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
def compilable?
|
||||||
def next_line lines, expects
|
false
|
||||||
while lines.any?
|
end
|
||||||
l = lines.shift.strip
|
|
||||||
next if (l.empty? and expects != :comment)
|
private
|
||||||
comment = (l =~ /^\|.*\|$/).nil?
|
def next_line lines, expects
|
||||||
if (comment and expects == :command) or (!comment and expects == :comment)
|
while lines.any?
|
||||||
lines.unshift l
|
l = lines.shift.strip
|
||||||
return nil
|
next if (l.empty? and expects != :comment)
|
||||||
end
|
comment = (l =~ /^\|.*\|$/).nil?
|
||||||
return l
|
if (comment and expects == :command) or (!comment and expects == :comment)
|
||||||
end
|
lines.unshift l
|
||||||
end
|
return nil
|
||||||
|
end
|
||||||
def extract_comments lines
|
return l
|
||||||
comments = ''
|
end
|
||||||
while (line = next_line lines, :comment)
|
end
|
||||||
comments << line + "\n"
|
|
||||||
end
|
def extract_comments lines
|
||||||
if defined? RedCloth
|
comments = ''
|
||||||
comments = RedCloth.new(comments).to_html
|
while (line = next_line lines, :comment)
|
||||||
end
|
comments << line + "\n"
|
||||||
comments += "\n" unless comments.empty?
|
end
|
||||||
comments
|
if defined? RedCloth
|
||||||
end
|
comments = RedCloth.new(comments).to_html
|
||||||
|
end
|
||||||
def extract_commands lines, name
|
comments += "\n" unless comments.empty?
|
||||||
html = "<table>\n<tr><th colspan=\"3\">#{name}</th></tr>\n"
|
comments
|
||||||
while (line = next_line lines, :command)
|
end
|
||||||
line = line[1..-2] #remove starting and ending |
|
|
||||||
cells = line.split '|'
|
def extract_commands lines, name
|
||||||
if cells.first == 'includePartial'
|
html = "<table>\n<tr><th colspan=\"3\">#{name}</th></tr>\n"
|
||||||
html << include_partial(cells[1..-1])
|
while (line = next_line lines, :command)
|
||||||
next
|
line = line[1..-2] #remove starting and ending |
|
||||||
end
|
cells = line.split '|'
|
||||||
raise 'There might only be a maximum of three cells!' if cells.length > 3
|
if cells.first == 'includePartial'
|
||||||
html << '<tr>'
|
html << include_partial(cells[1..-1])
|
||||||
(1..3).each do
|
next
|
||||||
cell = cells.shift
|
end
|
||||||
cell = (cell ? CGI.escapeHTML(cell.strip) : ' ')
|
raise 'There might only be a maximum of three cells!' if cells.length > 3
|
||||||
html << "<td>#{cell}</td>"
|
html << '<tr>'
|
||||||
end
|
(1..3).each do
|
||||||
html << "</tr>\n"
|
cell = cells.shift
|
||||||
end
|
cell = (cell ? CGI.escapeHTML(cell.strip) : ' ')
|
||||||
html << "</table>\n"
|
html << "<td>#{cell}</td>"
|
||||||
end
|
end
|
||||||
|
html << "</tr>\n"
|
||||||
def include_partial params
|
end
|
||||||
partial = params.shift
|
html << "</table>\n"
|
||||||
locals = {}
|
end
|
||||||
params.each do |assignment|
|
|
||||||
next if assignment.empty?
|
def include_partial params
|
||||||
_, var, value = assignment.split(/^([a-z_][a-zA-Z0-9_]*)\s*=\s*(.*)$/)
|
partial = params.shift
|
||||||
raise "Invalid format '#{assignment}'. Should be '|includePartial|partial|var1=value|var2=value|." unless var
|
locals = {}
|
||||||
locals[var.to_sym] = value or ''
|
params.each do |assignment|
|
||||||
end
|
next if assignment.empty?
|
||||||
@view.render :partial => partial, :locals => locals
|
_, var, value = assignment.split(/^([a-z_][a-zA-Z0-9_]*)\s*=\s*(.*)$/)
|
||||||
end
|
raise "Invalid format '#{assignment}'. Should be '|includePartial|partial|var1=value|var2=value|." unless var
|
||||||
|
locals[var.to_sym] = value or ''
|
||||||
|
end
|
||||||
|
@view.render :partial => partial, :locals => locals
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
4
vendor/plugins/will_paginate/.gitignore
vendored
4
vendor/plugins/will_paginate/.gitignore
vendored
|
@ -1,2 +1,4 @@
|
||||||
/pkg
|
|
||||||
/doc
|
/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 'rubygems'
|
||||||
require 'rake/testtask'
|
begin
|
||||||
require 'rake/rdoctask'
|
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.'
|
desc 'Default: run unit tests.'
|
||||||
task :default => :test
|
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.'
|
desc 'Generate RDoc documentation for the will_paginate plugin.'
|
||||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||||
files = ['README', 'LICENSE', 'lib/**/*.rb']
|
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG').
|
||||||
rdoc.rdoc_files.add(files)
|
include('lib/**/*.rb').
|
||||||
rdoc.main = "README" # page to start on
|
exclude('lib/will_paginate/named_scope*').
|
||||||
rdoc.title = "will_paginate"
|
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.main = "README.rdoc" # page to start on
|
||||||
rdoc.template = templates.find { |t| File.exists? t }
|
rdoc.title = "will_paginate documentation"
|
||||||
|
|
||||||
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
||||||
rdoc.options << '--inline-source'
|
rdoc.options << '--inline-source' << '--charset=UTF-8'
|
||||||
rdoc.options << '--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
|
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'
|
require 'will_paginate'
|
||||||
WillPaginate.enable
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ module WillPaginate
|
||||||
require 'will_paginate/view_helpers'
|
require 'will_paginate/view_helpers'
|
||||||
ActionView::Base.class_eval { include ViewHelpers }
|
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
|
ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -33,28 +33,45 @@ module WillPaginate
|
||||||
require 'will_paginate/finder'
|
require 'will_paginate/finder'
|
||||||
ActiveRecord::Base.class_eval { include Finder }
|
ActiveRecord::Base.class_eval { include Finder }
|
||||||
|
|
||||||
associations = ActiveRecord::Associations
|
# support pagination on associations
|
||||||
collection = associations::AssociationCollection
|
a = ActiveRecord::Associations
|
||||||
|
returning([ a::AssociationCollection ]) { |classes|
|
||||||
# to support paginating finders on associations, we have to mix in the
|
# detect http://dev.rubyonrails.org/changeset/9230
|
||||||
# method_missing magic from WillPaginate::Finder::ClassMethods to AssociationProxy
|
unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
|
||||||
# subclasses, but in a different way for Rails 1.2.x and 2.0
|
classes << a::HasManyThroughAssociation
|
||||||
(collection.instance_methods.include?(:create!) ?
|
end
|
||||||
collection : collection.subclasses.map(&:constantize)
|
}.each do |klass|
|
||||||
).push(associations::HasManyThroughAssociation).each do |klass|
|
|
||||||
klass.class_eval do
|
klass.class_eval do
|
||||||
include Finder::ClassMethods
|
include Finder::ClassMethods
|
||||||
alias_method_chain :method_missing, :paginate
|
alias_method_chain :method_missing, :paginate
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
module Deprecation #:nodoc:
|
module Deprecation #:nodoc:
|
||||||
extend ActiveSupport::Deprecation
|
extend ActiveSupport::Deprecation
|
||||||
|
|
||||||
def self.warn(message, callstack = caller)
|
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?
|
behavior.call(message, callstack) if behavior && !silenced?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -63,3 +80,7 @@ module WillPaginate
|
||||||
end
|
end
|
||||||
end
|
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
|
module WillPaginate
|
||||||
# = OMG, invalid page number!
|
# = Invalid page number error
|
||||||
# This is an ArgumentError raised in case a page was requested that is either
|
# 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
|
# zero or negative number. You should decide how do deal with such errors in
|
||||||
# the controller.
|
# 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
|
# 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
|
# requested. Use <tt>WillPaginate::Collection#out_of_bounds?</tt> method to
|
||||||
# check for those cases and manually deal with them as you see fit.
|
# check for those cases and manually deal with them as you see fit.
|
||||||
|
@ -15,26 +22,34 @@ module WillPaginate
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Arrays returned from paginating finds are, in fact, instances of this.
|
# = The key to pagination
|
||||||
# You may think of WillPaginate::Collection as an ordinary array with some
|
# Arrays returned from paginating finds are, in fact, instances of this little
|
||||||
# extra properties. Those properties are used by view helpers to generate
|
# 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.
|
# correct page links.
|
||||||
#
|
#
|
||||||
# WillPaginate::Collection also assists in rolling out your own pagination
|
# WillPaginate::Collection also assists in rolling out your own pagination
|
||||||
# solutions: see +create+.
|
# 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
|
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
|
# 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
|
# is best to do lazy counting; in other words, count *conditionally* after
|
||||||
# populating the collection using the +replace+ method.
|
# populating the collection using the +replace+ method.
|
||||||
#
|
|
||||||
def initialize(page, per_page, total = nil)
|
def initialize(page, per_page, total = nil)
|
||||||
@current_page = page.to_i
|
@current_page = page.to_i
|
||||||
raise InvalidPage.new(page, @current_page) if @current_page < 1
|
raise InvalidPage.new(page, @current_page) if @current_page < 1
|
||||||
@per_page = per_page.to_i
|
@per_page = per_page.to_i
|
||||||
raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
|
raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
|
||||||
|
|
||||||
self.total_entries = total if total
|
self.total_entries = total if total
|
||||||
|
@ -65,29 +80,25 @@ module WillPaginate
|
||||||
# end
|
# end
|
||||||
# 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)
|
def self.create(page, per_page, total = nil, &block)
|
||||||
pager = new(page, per_page, total)
|
pager = new(page, per_page, total)
|
||||||
yield pager
|
yield pager
|
||||||
pager
|
pager
|
||||||
end
|
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
|
# 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
|
# larger number than the last page. Can be used in combination with flashes
|
||||||
# and redirecting.
|
# and redirecting.
|
||||||
def out_of_bounds?
|
def out_of_bounds?
|
||||||
current_page > page_count
|
current_page > total_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
# Current offset of the paginated collection. If we're on the first page,
|
# 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,
|
# 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
|
# the offset is 30. This property is useful if you want to render ordinals
|
||||||
# besides your records: simply start with offset + 1.
|
# besides your records: simply start with offset + 1.
|
||||||
#
|
|
||||||
def offset
|
def offset
|
||||||
(current_page - 1) * per_page
|
(current_page - 1) * per_page
|
||||||
end
|
end
|
||||||
|
@ -99,7 +110,7 @@ module WillPaginate
|
||||||
|
|
||||||
# current_page + 1 or nil if there is no next page
|
# current_page + 1 or nil if there is no next page
|
||||||
def next_page
|
def next_page
|
||||||
current_page < page_count ? (current_page + 1) : nil
|
current_page < total_pages ? (current_page + 1) : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def total_entries=(number)
|
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
|
# +total_entries+ and set it to a proper value if it's +nil+. See the example
|
||||||
# in +create+.
|
# in +create+.
|
||||||
def replace(array)
|
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!
|
# The collection is shorter then page limit? Rejoice, because
|
||||||
if total_entries.nil? and length > 0 and length < per_page
|
# then we know that we are on the last page!
|
||||||
self.total_entries = offset + length
|
if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
|
||||||
end
|
self.total_entries = offset + length
|
||||||
end
|
end
|
||||||
|
|
||||||
|
result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
require 'will_paginate'
|
|
||||||
require 'set'
|
require 'set'
|
||||||
|
require 'will_paginate/array'
|
||||||
|
|
||||||
unless Hash.instance_methods.include? 'except'
|
unless Hash.instance_methods.include? 'except'
|
||||||
Hash.class_eval do
|
Hash.class_eval do
|
||||||
|
@ -30,51 +30,3 @@ unless Hash.instance_methods.include? 'slice'
|
||||||
end
|
end
|
||||||
end
|
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
|
module WillPaginate
|
||||||
# A mixin for ActiveRecord::Base. Provides +per_page+ class method
|
# 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
|
# Find out more in WillPaginate::Finder::ClassMethods
|
||||||
#
|
#
|
||||||
|
@ -18,9 +18,9 @@ module WillPaginate
|
||||||
|
|
||||||
# = Paginating finders for ActiveRecord models
|
# = Paginating finders for ActiveRecord models
|
||||||
#
|
#
|
||||||
# WillPaginate adds +paginate+ and +per_page+ methods to ActiveRecord::Base
|
# WillPaginate adds +paginate+, +per_page+ and other methods to
|
||||||
# class methods and associations. It also hooks into +method_missing+ to
|
# ActiveRecord::Base class methods and associations. It also hooks into
|
||||||
# intercept pagination calls to dynamic finders such as
|
# +method_missing+ to intercept pagination calls to dynamic finders such as
|
||||||
# +paginate_by_user_id+ and translate them to ordinary finders
|
# +paginate_by_user_id+ and translate them to ordinary finders
|
||||||
# (+find_all_by_user_id+ in this case).
|
# (+find_all_by_user_id+ in this case).
|
||||||
#
|
#
|
||||||
|
@ -85,6 +85,31 @@ module WillPaginate
|
||||||
pager.total_entries = wp_count(count_options, args, finder) unless pager.total_entries
|
pager.total_entries = wp_count(count_options, args, finder) unless pager.total_entries
|
||||||
end
|
end
|
||||||
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
|
# 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
|
# based on the params otherwise used by paginating finds: +page+ and
|
||||||
|
@ -159,19 +184,27 @@ module WillPaginate
|
||||||
unless options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
|
unless options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
|
||||||
excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
|
excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
|
||||||
end
|
end
|
||||||
|
|
||||||
# count expects (almost) the same options as find
|
# count expects (almost) the same options as find
|
||||||
count_options = options.except *excludees
|
count_options = options.except *excludees
|
||||||
|
|
||||||
# merge the hash found in :count
|
# merge the hash found in :count
|
||||||
# this allows you to specify :select, :order, or anything else just for the count query
|
# this allows you to specify :select, :order, or anything else just for the count query
|
||||||
count_options.update options[:count] if options[:count]
|
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 ...
|
# we may have to scope ...
|
||||||
counter = Proc.new { count(count_options) }
|
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'))
|
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
|
# 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
|
# 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>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
|
||||||
# * <tt>:params</tt> -- additional parameters when generating pagination links
|
# * <tt>:params</tt> -- additional parameters when generating pagination links
|
||||||
# (eg. <tt>:controller => "foo", :action => nil</tt>)
|
# (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>: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
|
# * <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)
|
# 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
|
# * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID
|
||||||
# generated from the class name of objects in collection: for example, paginating
|
# automatically generated from the class name of objects in collection: for example, paginating
|
||||||
# ArticleComment models would yield an ID of "article_comments_pagination".
|
# ArticleComment models would yield an ID of "article_comments_pagination".
|
||||||
#
|
#
|
||||||
# All options beside listed ones are passed as HTML attributes to the container
|
# All options beside listed ones are passed as HTML attributes to the container
|
||||||
|
@ -85,29 +86,99 @@ module WillPaginate
|
||||||
collection_name = "@#{controller.controller_name}"
|
collection_name = "@#{controller.controller_name}"
|
||||||
collection = instance_variable_get(collection_name)
|
collection = instance_variable_get(collection_name)
|
||||||
raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
|
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
|
end
|
||||||
# early exit if there is nothing to render
|
# 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
|
options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options
|
||||||
# create the renderer instance
|
|
||||||
renderer_class = options[:renderer].to_s.constantize
|
# get the renderer instance
|
||||||
renderer = renderer_class.new collection, options, self
|
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
|
# render HTML for pagination
|
||||||
|
renderer.prepare collection, options, self
|
||||||
renderer.to_html
|
renderer.to_html
|
||||||
end
|
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.
|
# Renders a helpful message with numbers of displayed vs. total entries.
|
||||||
# You can use this as a blueprint for your own, similar helpers.
|
# You can use this as a blueprint for your own, similar helpers.
|
||||||
#
|
#
|
||||||
# <%= page_entries_info @posts %>
|
# <%= page_entries_info @posts %>
|
||||||
# #-> Displaying entries 6 - 10 of 26 in total
|
# #-> Displaying posts 6 - 10 of 26 in total
|
||||||
def page_entries_info(collection)
|
#
|
||||||
%{Displaying entries <b>%d - %d</b> of <b>%d</b> in total} % [
|
# By default, the message will use the humanized class name of objects
|
||||||
collection.offset + 1,
|
# in collection: for instance, "project types" for ProjectType models.
|
||||||
collection.offset + collection.length,
|
# Override this to your liking with the <tt>:entry_name</tt> parameter:
|
||||||
collection.total_entries
|
#
|
||||||
]
|
# <%= 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
|
||||||
|
]
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -115,22 +186,43 @@ module WillPaginate
|
||||||
# links. It is used by +will_paginate+ helper internally.
|
# links. It is used by +will_paginate+ helper internally.
|
||||||
class LinkRenderer
|
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
|
@collection = collection
|
||||||
@options = options
|
@options = options
|
||||||
@template = template
|
@template = template
|
||||||
|
|
||||||
|
# reset values in case we're re-using this instance
|
||||||
|
@total_pages = @param_name = @url_string = nil
|
||||||
end
|
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
|
def to_html
|
||||||
links = @options[:page_links] ? windowed_links : []
|
links = @options[:page_links] ? windowed_links : []
|
||||||
# previous/next buttons
|
# previous/next buttons
|
||||||
links.unshift page_link_or_span(@collection.previous_page, 'disabled', @options[:prev_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', @options[:next_label])
|
links.push page_link_or_span(@collection.next_page, 'disabled next_page', @options[:next_label])
|
||||||
|
|
||||||
html = links.join(@options[:separator])
|
html = links.join(@options[:separator])
|
||||||
@options[:container] ? @template.content_tag(:div, html, html_attributes) : html
|
@options[:container] ? @template.content_tag(:div, html, html_attributes) : html
|
||||||
end
|
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
|
def html_attributes
|
||||||
return @html_attributes if @html_attributes
|
return @html_attributes if @html_attributes
|
||||||
@html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
|
@html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
|
||||||
|
@ -143,20 +235,21 @@ module WillPaginate
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def gap_marker; '...'; end
|
# Collects link items for visible page numbers.
|
||||||
|
|
||||||
def windowed_links
|
def windowed_links
|
||||||
prev = nil
|
prev = nil
|
||||||
|
|
||||||
visible_page_numbers.inject [] do |links, n|
|
visible_page_numbers.inject [] do |links, n|
|
||||||
# detect gaps:
|
# detect gaps:
|
||||||
links << gap_marker if prev and n > prev + 1
|
links << gap_marker if prev and n > prev + 1
|
||||||
links << page_link_or_span(n)
|
links << page_link_or_span(n, 'current')
|
||||||
prev = n
|
prev = n
|
||||||
links
|
links
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Calculates visible page numbers using the <tt>:inner_window</tt> and
|
||||||
|
# <tt>:outer_window</tt> options.
|
||||||
def visible_page_numbers
|
def visible_page_numbers
|
||||||
inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
|
inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
|
||||||
window_from = current_page - inner_window
|
window_from = current_page - inner_window
|
||||||
|
@ -166,9 +259,11 @@ module WillPaginate
|
||||||
if window_to > total_pages
|
if window_to > total_pages
|
||||||
window_from -= window_to - total_pages
|
window_from -= window_to - total_pages
|
||||||
window_to = total_pages
|
window_to = total_pages
|
||||||
elsif window_from < 1
|
end
|
||||||
|
if window_from < 1
|
||||||
window_to += 1 - window_from
|
window_to += 1 - window_from
|
||||||
window_from = 1
|
window_from = 1
|
||||||
|
window_to = total_pages if window_to > total_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
visible = (1..total_pages).to_a
|
visible = (1..total_pages).to_a
|
||||||
|
@ -180,21 +275,63 @@ module WillPaginate
|
||||||
visible
|
visible
|
||||||
end
|
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
|
text ||= page.to_s
|
||||||
|
|
||||||
if page and page != current_page
|
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
|
else
|
||||||
@template.content_tag :span, text, :class => span_class
|
page_span page, text, :class => span_class
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_options(page)
|
def page_link(page, text, attributes = {})
|
||||||
options = { param_name => page }
|
@template.link_to text, url_for(page), attributes
|
||||||
# page links should preserve GET parameters
|
end
|
||||||
options = params.merge(options) if @template.request.get?
|
|
||||||
options.rec_merge!(@options[:params]) if @options[:params]
|
def page_span(page, text, attributes = {})
|
||||||
return options
|
@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
|
||||||
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -212,15 +349,25 @@ module WillPaginate
|
||||||
end
|
end
|
||||||
|
|
||||||
def total_pages
|
def total_pages
|
||||||
@collection.page_count
|
@total_pages ||= WillPaginate::ViewHelpers.total_pages_for_collection(@collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
def param_name
|
def param_name
|
||||||
@param_name ||= @options[:param_name].to_sym
|
@param_name ||= @options[:param_name].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def params
|
# Recursively merge into target hash by using stringified keys from the other one
|
||||||
@params ||= @template.params.to_hash.symbolize_keys
|
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
|
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'
|
gem 'activerecord'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
$:.unshift "#{plugin_root}/lib"
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
require File.dirname(__FILE__) + '/helper'
|
require 'helper'
|
||||||
require 'will_paginate/core_ext'
|
require 'will_paginate/array'
|
||||||
|
|
||||||
class ArrayPaginationTest < Test::Unit::TestCase
|
class ArrayPaginationTest < Test::Unit::TestCase
|
||||||
def test_simple
|
def test_simple
|
||||||
|
@ -11,7 +11,8 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
||||||
{ :page => 3, :per_page => 5, :expected => [] },
|
{ :page => 3, :per_page => 5, :expected => [] },
|
||||||
].
|
].
|
||||||
each do |conditions|
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,14 +23,8 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_deprecated_api
|
def test_deprecated_api
|
||||||
assert_deprecated 'paginate API' do
|
assert_raise(ArgumentError) { [].paginate(2) }
|
||||||
result = (1..50).to_a.paginate(2, 10)
|
assert_raise(ArgumentError) { [].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 }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_total_entries_has_precedence
|
def test_total_entries_has_precedence
|
||||||
|
@ -50,14 +45,28 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal entries, collection
|
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_kind_of Array, collection
|
||||||
assert_instance_of Array, collection.entries
|
assert_instance_of Array, collection.entries
|
||||||
assert_equal 3, collection.offset
|
assert_equal 3, collection.offset
|
||||||
assert_equal 4, collection.page_count
|
assert_equal 4, collection.total_pages
|
||||||
assert !collection.out_of_bounds?
|
assert !collection.out_of_bounds?
|
||||||
end
|
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
|
def test_out_of_bounds
|
||||||
entries = create(2, 3, 2){}
|
entries = create(2, 3, 2){}
|
||||||
assert entries.out_of_bounds?
|
assert entries.out_of_bounds?
|
||||||
|
@ -90,13 +99,20 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
||||||
pager.replace array(0)
|
pager.replace array(0)
|
||||||
end
|
end
|
||||||
assert_equal nil, entries.total_entries
|
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
|
end
|
||||||
|
|
||||||
def test_invalid_page
|
def test_invalid_page
|
||||||
bad_input = [0, -1, nil, '', 'Schnitzel']
|
bad_inputs = [0, -1, nil, '', 'Schnitzel']
|
||||||
|
|
||||||
bad_input.each do |bad|
|
bad_inputs.each do |bad|
|
||||||
assert_raise(WillPaginate::InvalidPage) { create(bad) }
|
assert_raise(WillPaginate::InvalidPage) { create bad }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -104,6 +120,11 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
||||||
assert_raise(ArgumentError) { create(1, -1) }
|
assert_raise(ArgumentError) { create(1, -1) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_page_count_was_removed
|
||||||
|
assert_raise(NoMethodError) { create.page_count }
|
||||||
|
# It's `total_pages` now.
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def create(page = 2, limit = 5, total = nil, &block)
|
def create(page = 2, limit = 5, total = nil, &block)
|
||||||
if block_given?
|
if block_given?
|
||||||
|
@ -116,16 +137,4 @@ class ArrayPaginationTest < Test::Unit::TestCase
|
||||||
def array(size = 3)
|
def array(size = 3)
|
||||||
Array.new(size)
|
Array.new(size)
|
||||||
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
|
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
|
#!/usr/bin/env ruby
|
||||||
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
||||||
libs = []
|
libs = []
|
||||||
dirname = File.dirname(__FILE__)
|
|
||||||
|
|
||||||
libs << 'irb/completion'
|
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"
|
||||||
|
|
222
vendor/plugins/will_paginate/test/finder_test.rb
vendored
222
vendor/plugins/will_paginate/test/finder_test.rb
vendored
|
@ -1,8 +1,9 @@
|
||||||
require File.dirname(__FILE__) + '/helper'
|
require 'helper'
|
||||||
require File.dirname(__FILE__) + '/lib/activerecord_test_case'
|
require 'lib/activerecord_test_case'
|
||||||
|
|
||||||
require 'will_paginate'
|
require 'will_paginate'
|
||||||
WillPaginate.enable_activerecord
|
WillPaginate.enable_activerecord
|
||||||
|
WillPaginate.enable_named_scope
|
||||||
|
|
||||||
class FinderTest < ActiveRecordTestCase
|
class FinderTest < ActiveRecordTestCase
|
||||||
fixtures :topics, :replies, :users, :projects, :developers_projects
|
fixtures :topics, :replies, :users, :projects, :developers_projects
|
||||||
|
@ -12,18 +13,18 @@ class FinderTest < ActiveRecordTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_simple_paginate
|
def test_simple_paginate
|
||||||
entries = Topic.paginate :page => nil
|
assert_queries(1) do
|
||||||
assert_equal 1, entries.current_page
|
entries = Topic.paginate :page => nil
|
||||||
assert_nil entries.previous_page
|
assert_equal 1, entries.current_page
|
||||||
assert_nil entries.next_page
|
assert_equal 1, entries.total_pages
|
||||||
assert_equal 1, entries.page_count
|
assert_equal 4, entries.size
|
||||||
assert_equal 4, entries.size
|
end
|
||||||
|
|
||||||
entries = Topic.paginate :page => 2
|
assert_queries(2) do
|
||||||
assert_equal 2, entries.current_page
|
entries = Topic.paginate :page => 2
|
||||||
assert_equal 1, entries.previous_page
|
assert_equal 1, entries.total_pages
|
||||||
assert_equal 1, entries.page_count
|
assert entries.empty?
|
||||||
assert entries.empty?
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_parameter_api
|
def test_parameter_api
|
||||||
|
@ -41,31 +42,31 @@ class FinderTest < ActiveRecordTestCase
|
||||||
def test_paginate_with_per_page
|
def test_paginate_with_per_page
|
||||||
entries = Topic.paginate :page => 1, :per_page => 1
|
entries = Topic.paginate :page => 1, :per_page => 1
|
||||||
assert_equal 1, entries.size
|
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
|
# Developer class has explicit per_page at 10
|
||||||
entries = Developer.paginate :page => 1
|
entries = Developer.paginate :page => 1
|
||||||
assert_equal 10, entries.size
|
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
|
entries = Developer.paginate :page => 1, :per_page => 5
|
||||||
assert_equal 11, entries.total_entries
|
assert_equal 11, entries.total_entries
|
||||||
assert_equal 5, entries.size
|
assert_equal 5, entries.size
|
||||||
assert_equal 3, entries.page_count
|
assert_equal 3, entries.total_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_paginate_with_order
|
def test_paginate_with_order
|
||||||
entries = Topic.paginate :page => 1, :order => 'created_at desc'
|
entries = Topic.paginate :page => 1, :order => 'created_at desc'
|
||||||
expected = [topics(:futurama), topics(:harvey_birdman), topics(:rails), topics(:ar)].reverse
|
expected = [topics(:futurama), topics(:harvey_birdman), topics(:rails), topics(:ar)].reverse
|
||||||
assert_equal expected, entries.to_a
|
assert_equal expected, entries.to_a
|
||||||
assert_equal 1, entries.page_count
|
assert_equal 1, entries.total_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_paginate_with_conditions
|
def test_paginate_with_conditions
|
||||||
entries = Topic.paginate :page => 1, :conditions => ["created_at > ?", 30.minutes.ago]
|
entries = Topic.paginate :page => 1, :conditions => ["created_at > ?", 30.minutes.ago]
|
||||||
expected = [topics(:rails), topics(:ar)]
|
expected = [topics(:rails), topics(:ar)]
|
||||||
assert_equal expected, entries.to_a
|
assert_equal expected, entries.to_a
|
||||||
assert_equal 1, entries.page_count
|
assert_equal 1, entries.total_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_paginate_with_include_and_conditions
|
def test_paginate_with_include_and_conditions
|
||||||
|
@ -85,11 +86,14 @@ class FinderTest < ActiveRecordTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_paginate_with_include_and_order
|
def test_paginate_with_include_and_order
|
||||||
entries = Topic.paginate \
|
entries = nil
|
||||||
:page => 1,
|
assert_queries(2) do
|
||||||
:include => :replies,
|
entries = Topic.paginate \
|
||||||
:order => 'replies.created_at asc, topics.created_at asc',
|
:page => 1,
|
||||||
:per_page => 10
|
:include => :replies,
|
||||||
|
:order => 'replies.created_at asc, topics.created_at asc',
|
||||||
|
:per_page => 10
|
||||||
|
end
|
||||||
|
|
||||||
expected = Topic.find :all,
|
expected = Topic.find :all,
|
||||||
:include => 'replies',
|
:include => 'replies',
|
||||||
|
@ -104,7 +108,7 @@ class FinderTest < ActiveRecordTestCase
|
||||||
entries, project = nil, projects(:active_record)
|
entries, project = nil, projects(:active_record)
|
||||||
|
|
||||||
assert_nothing_raised "THIS IS A BUG in Rails 1.2.3 that was fixed in [7326]. " +
|
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 \
|
entries = project.topics.paginate \
|
||||||
:page => 1,
|
:page => 1,
|
||||||
:include => :replies,
|
:include => :replies,
|
||||||
|
@ -125,10 +129,12 @@ class FinderTest < ActiveRecordTestCase
|
||||||
expected_name_ordered = [projects(:action_controller), projects(:active_record)]
|
expected_name_ordered = [projects(:action_controller), projects(:active_record)]
|
||||||
expected_id_ordered = [projects(:active_record), projects(:action_controller)]
|
expected_id_ordered = [projects(:active_record), projects(:action_controller)]
|
||||||
|
|
||||||
# with association-specified order
|
assert_queries(2) do
|
||||||
entries = dhh.projects.paginate(:page => 1)
|
# with association-specified order
|
||||||
assert_equal expected_name_ordered, entries
|
entries = dhh.projects.paginate(:page => 1)
|
||||||
assert_equal 2, entries.total_entries
|
assert_equal expected_name_ordered, entries
|
||||||
|
assert_equal 2, entries.total_entries
|
||||||
|
end
|
||||||
|
|
||||||
# with explicit order
|
# with explicit order
|
||||||
entries = dhh.projects.paginate(:page => 1, :order => 'projects.id')
|
entries = dhh.projects.paginate(:page => 1, :order => 'projects.id')
|
||||||
|
@ -148,29 +154,43 @@ class FinderTest < ActiveRecordTestCase
|
||||||
|
|
||||||
def test_paginate_association_extension
|
def test_paginate_association_extension
|
||||||
project = Project.find(:first)
|
project = Project.find(:first)
|
||||||
entries = project.replies.paginate_recent :page => 1
|
|
||||||
assert_equal [replies(:brave)], entries
|
assert_queries(2) do
|
||||||
|
entries = project.replies.paginate_recent :page => 1
|
||||||
|
assert_equal [replies(:brave)], entries
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_paginate_with_joins
|
def test_paginate_with_joins
|
||||||
entries = Developer.paginate :page => 1,
|
entries = nil
|
||||||
:joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
|
|
||||||
:conditions => 'project_id = 1'
|
assert_queries(1) do
|
||||||
assert_equal 2, entries.size
|
entries = Developer.paginate :page => 1,
|
||||||
developer_names = entries.map { |d| d.name }
|
:joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
|
||||||
assert developer_names.include?('David')
|
:conditions => 'project_id = 1'
|
||||||
assert developer_names.include?('Jamis')
|
assert_equal 2, entries.size
|
||||||
|
developer_names = entries.map &:name
|
||||||
|
assert developer_names.include?('David')
|
||||||
|
assert developer_names.include?('Jamis')
|
||||||
|
end
|
||||||
|
|
||||||
expected = entries.to_a
|
assert_queries(1) do
|
||||||
entries = Developer.paginate :page => 1,
|
expected = entries.to_a
|
||||||
:joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
|
entries = Developer.paginate :page => 1,
|
||||||
:conditions => 'project_id = 1', :count => { :select => "users.id" }
|
:joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
|
||||||
assert_equal expected, entries.to_a
|
:conditions => 'project_id = 1', :count => { :select => "users.id" }
|
||||||
|
assert_equal expected, entries.to_a
|
||||||
|
assert_equal 2, entries.total_entries
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_paginate_with_group
|
def test_paginate_with_group
|
||||||
entries = Developer.paginate :page => 1, :per_page => 10,
|
entries = nil
|
||||||
:group => 'salary', :select => 'salary', :order => 'salary'
|
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
|
expected = [ users(:david), users(:jamis), users(:dev_10), users(:poor_jamis) ].map(&:salary).sort
|
||||||
assert_equal expected, entries.map(&:salary)
|
assert_equal expected, entries.map(&:salary)
|
||||||
end
|
end
|
||||||
|
@ -201,6 +221,56 @@ class FinderTest < ActiveRecordTestCase
|
||||||
assert_equal 2, entries.total_entries
|
assert_equal 2, entries.total_entries
|
||||||
end
|
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
|
def test_readonly
|
||||||
assert_nothing_raised { Developer.paginate :readonly => true, :page => 1 }
|
assert_nothing_raised { Developer.paginate :readonly => true, :page => 1 }
|
||||||
end
|
end
|
||||||
|
@ -216,13 +286,15 @@ class FinderTest < ActiveRecordTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
# Is this Rails 2.0? Find out by testing find_all which was removed in [6998]
|
# 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
|
def test_paginate_array_of_ids
|
||||||
# AR finders also accept arrays of IDs
|
# AR finders also accept arrays of IDs
|
||||||
# (this was broken in Rails before [6912])
|
# (this was broken in Rails before [6912])
|
||||||
entries = Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id')
|
assert_queries(1) do
|
||||||
assert_equal (4..6).to_a, entries.map(&:id)
|
entries = Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id')
|
||||||
assert_equal 8, entries.total_entries
|
assert_equal (4..6).to_a, entries.map(&:id)
|
||||||
|
assert_equal 8, entries.total_entries
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -230,7 +302,7 @@ class FinderTest < ActiveRecordTestCase
|
||||||
def test_implicit_all_with_dynamic_finders
|
def test_implicit_all_with_dynamic_finders
|
||||||
Topic.expects(:find_all_by_foo).returns([])
|
Topic.expects(:find_all_by_foo).returns([])
|
||||||
Topic.expects(:count).returns(0)
|
Topic.expects(:count).returns(0)
|
||||||
Topic.paginate_by_foo :page => 1
|
Topic.paginate_by_foo :page => 2
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_guessing_the_total_count
|
def test_guessing_the_total_count
|
||||||
|
@ -241,6 +313,14 @@ class FinderTest < ActiveRecordTestCase
|
||||||
assert_equal 6, entries.total_entries
|
assert_equal 6, entries.total_entries
|
||||||
end
|
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
|
def test_extra_parameters_stay_untouched
|
||||||
Topic.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5))
|
Topic.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5))
|
||||||
Topic.expects(:count).with({:foo => 'bar'}).returns(1)
|
Topic.expects(:count).with({:foo => 'bar'}).returns(1)
|
||||||
|
@ -251,13 +331,13 @@ class FinderTest < ActiveRecordTestCase
|
||||||
def test_count_skips_select
|
def test_count_skips_select
|
||||||
Developer.stubs(:find).returns([])
|
Developer.stubs(:find).returns([])
|
||||||
Developer.expects(:count).with({}).returns(0)
|
Developer.expects(:count).with({}).returns(0)
|
||||||
Developer.paginate :select => 'salary', :page => 1
|
Developer.paginate :select => 'salary', :page => 2
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_count_select_when_distinct
|
def test_count_select_when_distinct
|
||||||
Developer.stubs(:find).returns([])
|
Developer.stubs(:find).returns([])
|
||||||
Developer.expects(:count).with(:select => 'DISTINCT salary').returns(0)
|
Developer.expects(:count).with(:select => 'DISTINCT salary').returns(0)
|
||||||
Developer.paginate :select => 'DISTINCT salary', :page => 1
|
Developer.paginate :select => 'DISTINCT salary', :page => 2
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_should_use_scoped_finders_if_present
|
def test_should_use_scoped_finders_if_present
|
||||||
|
@ -288,16 +368,16 @@ class FinderTest < ActiveRecordTestCase
|
||||||
Developer.expects(:find_by_sql).returns([])
|
Developer.expects(:find_by_sql).returns([])
|
||||||
Developer.expects(:count_by_sql).with("SELECT COUNT(*) FROM (sql\n ) AS count_table").returns(0)
|
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
|
end
|
||||||
|
|
||||||
# TODO: counts are still wrong
|
# TODO: counts are still wrong
|
||||||
def test_ability_to_use_with_custom_finders
|
def test_ability_to_use_with_custom_finders
|
||||||
# acts_as_taggable defines find_tagged_with(tag, options)
|
# 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.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
|
end
|
||||||
|
|
||||||
def test_array_argument_doesnt_eliminate_count
|
def test_array_argument_doesnt_eliminate_count
|
||||||
|
@ -310,7 +390,6 @@ class FinderTest < ActiveRecordTestCase
|
||||||
|
|
||||||
def test_paginating_finder_doesnt_mangle_options
|
def test_paginating_finder_doesnt_mangle_options
|
||||||
Developer.expects(:find).returns([])
|
Developer.expects(:find).returns([])
|
||||||
Developer.expects(:count).returns(0)
|
|
||||||
options = { :page => 1 }
|
options = { :page => 1 }
|
||||||
options.expects(:delete).never
|
options.expects(:delete).never
|
||||||
options_before = options.dup
|
options_before = options.dup
|
||||||
|
@ -318,5 +397,38 @@ class FinderTest < ActiveRecordTestCase
|
||||||
Developer.paginate(options)
|
Developer.paginate(options)
|
||||||
assert_equal options, options_before
|
assert_equal options, options_before
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,5 +7,7 @@ class Developer < User
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
named_scope :poor, :conditions => ['salary <= ?', 80000], :order => 'salary'
|
||||||
|
|
||||||
def self.per_page() 10 end
|
def self.per_page() 10 end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
action_controller:
|
|
||||||
id: 2
|
|
||||||
name: Active Controller
|
|
||||||
|
|
||||||
active_record:
|
active_record:
|
||||||
id: 1
|
id: 1
|
||||||
name: Active Record
|
name: Active Record
|
||||||
|
action_controller:
|
||||||
|
id: 2
|
||||||
|
name: Active Controller
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
class Reply < ActiveRecord::Base
|
class Reply < ActiveRecord::Base
|
||||||
belongs_to :topic, :include => [:replies]
|
belongs_to :topic, :include => [:replies]
|
||||||
|
|
||||||
|
named_scope :recent, :conditions => ['replies.created_at > ?', 15.minutes.ago]
|
||||||
|
|
||||||
validates_presence_of :content
|
validates_presence_of :content
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
class Topic < ActiveRecord::Base
|
class Topic < ActiveRecord::Base
|
||||||
has_many :replies, :dependent => :destroy, :order => 'replies.created_at DESC'
|
has_many :replies, :dependent => :destroy, :order => 'replies.created_at DESC'
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
|
|
||||||
|
named_scope :mentions_activerecord, :conditions => ['topics.title LIKE ?', '%ActiveRecord%']
|
||||||
end
|
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
|
# gem install redgreen for colored test output
|
||||||
begin require 'redgreen'; rescue LoadError; end
|
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
|
class Test::Unit::TestCase
|
||||||
protected
|
protected
|
||||||
|
@ -13,6 +13,18 @@ class Test::Unit::TestCase
|
||||||
[method.to_s, method.to_sym].each { |m| assert_respond_to object, m }
|
[method.to_s, method.to_sym].each { |m| assert_respond_to object, m }
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
# Wrap tests that use Mocha and skip if unavailable.
|
# 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
|
class ActiveRecordTestCase < Test::Unit::TestCase
|
||||||
# Set our fixture path
|
# Set our fixture path
|
||||||
|
@ -18,6 +18,19 @@ class ActiveRecordTestCase < Test::Unit::TestCase
|
||||||
# Default so Test::Unit::TestCase doesn't complain
|
# Default so Test::Unit::TestCase doesn't complain
|
||||||
def test_truth
|
def test_truth
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
ActiveRecordTestConnector.setup
|
ActiveRecordTestConnector.setup
|
||||||
|
|
|
@ -6,6 +6,8 @@ class ActiveRecordTestConnector
|
||||||
cattr_accessor :able_to_connect
|
cattr_accessor :able_to_connect
|
||||||
cattr_accessor :connected
|
cattr_accessor :connected
|
||||||
|
|
||||||
|
FIXTURES_PATH = File.join(File.dirname(__FILE__), '..', 'fixtures')
|
||||||
|
|
||||||
# Set our defaults
|
# Set our defaults
|
||||||
self.connected = false
|
self.connected = false
|
||||||
self.able_to_connect = true
|
self.able_to_connect = true
|
||||||
|
@ -14,13 +16,12 @@ class ActiveRecordTestConnector
|
||||||
unless self.connected || !self.able_to_connect
|
unless self.connected || !self.able_to_connect
|
||||||
setup_connection
|
setup_connection
|
||||||
load_schema
|
load_schema
|
||||||
# require_fixture_models
|
Dependencies.load_paths.unshift FIXTURES_PATH
|
||||||
Dependencies.load_paths.unshift(File.dirname(__FILE__) + "/../fixtures")
|
|
||||||
self.connected = true
|
self.connected = true
|
||||||
end
|
end
|
||||||
rescue Exception => e # errors from ActiveRecord setup
|
rescue Exception => e # errors from ActiveRecord setup
|
||||||
$stderr.puts "\nSkipping ActiveRecord assertion tests: #{e}"
|
$stderr.puts "\nSkipping ActiveRecord tests: #{e}"
|
||||||
#$stderr.puts " #{e.backtrace.join("\n ")}\n"
|
$stderr.puts "Install SQLite3 to run the full test suite for will_paginate.\n\n"
|
||||||
self.able_to_connect = false
|
self.able_to_connect = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ class ActiveRecordTestConnector
|
||||||
|
|
||||||
ActiveRecord::Base.establish_connection(configuration)
|
ActiveRecord::Base.establish_connection(configuration)
|
||||||
ActiveRecord::Base.configurations = { db => configuration }
|
ActiveRecord::Base.configurations = { db => configuration }
|
||||||
ActiveRecord::Base.connection
|
prepare ActiveRecord::Base.connection
|
||||||
|
|
||||||
unless Object.const_defined?(:QUOTED_TYPE)
|
unless Object.const_defined?(:QUOTED_TYPE)
|
||||||
Object.send :const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')
|
Object.send :const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')
|
||||||
|
@ -48,13 +49,21 @@ class ActiveRecordTestConnector
|
||||||
def self.load_schema
|
def self.load_schema
|
||||||
ActiveRecord::Base.silence do
|
ActiveRecord::Base.silence do
|
||||||
ActiveRecord::Migration.verbose = false
|
ActiveRecord::Migration.verbose = false
|
||||||
load File.dirname(__FILE__) + "/../fixtures/schema.rb"
|
load File.join(FIXTURES_PATH, 'schema.rb')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.require_fixture_models
|
def self.prepare(conn)
|
||||||
models = Dir.glob(File.dirname(__FILE__) + "/../fixtures/*.rb")
|
class << conn
|
||||||
models = (models.grep(/user.rb/) + models).uniq
|
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
|
||||||
models.each { |f| require f }
|
|
||||||
|
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
|
||||||
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 'boot'
|
||||||
require File.join(dirname, '..', 'boot')
|
require 'lib/activerecord_test_connector'
|
||||||
require File.join(dirname, 'activerecord_test_connector')
|
|
||||||
|
|
||||||
# setup the connection
|
# setup the connection
|
||||||
ActiveRecordTestConnector.setup
|
ActiveRecordTestConnector.setup
|
||||||
|
|
||||||
# load all fixtures
|
# load all fixtures
|
||||||
fixture_path = File.join(dirname, '..', 'fixtures')
|
Fixtures.create_fixtures(ActiveRecordTestConnector::FIXTURES_PATH, ActiveRecord::Base.connection.tables)
|
||||||
Fixtures.create_fixtures(fixture_path, ActiveRecord::Base.connection.tables)
|
|
||||||
|
|
||||||
require 'will_paginate'
|
require 'will_paginate'
|
||||||
WillPaginate.enable_activerecord
|
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"
|
desc "Run #{task_name} task for all projects"
|
||||||
task task_name do
|
task task_name do
|
||||||
PROJECTS.each do |project|
|
PROJECTS.each do |project|
|
||||||
system %(cd #{project} && #{env} rake #{task_name})
|
system %(cd #{project} && #{env} #{$0} #{task_name})
|
||||||
end
|
end
|
||||||
end
|
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)
|
*2.0.2* (December 16th, 2007)
|
||||||
|
|
||||||
* Included in Rails 2.0.2
|
* 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
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
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
|
recipients recipient
|
||||||
subject "[Signed up] Welcome #{recipient}"
|
subject "[Signed up] Welcome #{recipient}"
|
||||||
from "system@loudthinking.com"
|
from "system@loudthinking.com"
|
||||||
|
body :recipient => recipient
|
||||||
body(:recipient => recipient)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
The body of the email is created by using an Action View template (regular
|
The body of the email is created by using an Action View template (regular
|
||||||
|
@ -78,21 +77,26 @@ Example:
|
||||||
end
|
end
|
||||||
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)'
|
./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
|
== Configuration
|
||||||
|
|
||||||
The Base class has the full list of configuration options. Here's an example:
|
The Base class has the full list of configuration options. Here's an example:
|
||||||
|
|
||||||
ActionMailer::Base.smtp_settings = {
|
ActionMailer::Base.smtp_settings = {
|
||||||
:address=>'smtp.yourserver.com', # default: localhost
|
:address => 'smtp.yourserver.com', # default: localhost
|
||||||
:port=>'25', # default: 25
|
:port => '25', # default: 25
|
||||||
:user_name=>'user',
|
:user_name => 'user',
|
||||||
:password=>'pass',
|
:password => 'pass',
|
||||||
:authentication=>:plain # :plain, :login or :cram_md5
|
:authentication => :plain # :plain, :login or :cram_md5
|
||||||
}
|
}
|
||||||
|
|
||||||
== Dependencies
|
== 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/rdoctask'
|
||||||
require 'rake/packagetask'
|
require 'rake/packagetask'
|
||||||
require 'rake/gempackagetask'
|
require 'rake/gempackagetask'
|
||||||
require 'rake/contrib/rubyforgepublisher'
|
require 'rake/contrib/sshpublisher'
|
||||||
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
||||||
|
|
||||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||||
|
@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s|
|
||||||
s.rubyforge_project = "actionmailer"
|
s.rubyforge_project = "actionmailer"
|
||||||
s.homepage = "http://www.rubyonrails.org"
|
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.has_rdoc = true
|
||||||
s.requirements << 'none'
|
s.requirements << 'none'
|
||||||
|
@ -87,6 +87,7 @@ end
|
||||||
desc "Publish the release files to RubyForge."
|
desc "Publish the release files to RubyForge."
|
||||||
task :release => [ :package ] do
|
task :release => [ :package ] do
|
||||||
require 'rubyforge'
|
require 'rubyforge'
|
||||||
|
require 'rake/contrib/rubyforgepublisher'
|
||||||
|
|
||||||
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
|
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
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
# a copy of this software and associated documentation files (the
|
# a copy of this software and associated documentation files (the
|
||||||
|
|
|
@ -16,7 +16,7 @@ module ActionMailer
|
||||||
define_method(name) do |*parameters|
|
define_method(name) do |*parameters|
|
||||||
raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
|
raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
|
||||||
if parameters.empty?
|
if parameters.empty?
|
||||||
if instance_variables.include?(ivar)
|
if instance_variable_names.include?(ivar)
|
||||||
instance_variable_get(ivar)
|
instance_variable_get(ivar)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
245
vendor/rails/actionmailer/lib/action_mailer/base.rb
vendored
245
vendor/rails/actionmailer/lib/action_mailer/base.rb
vendored
|
@ -5,17 +5,17 @@ require 'action_mailer/utils'
|
||||||
require 'tmail/net'
|
require 'tmail/net'
|
||||||
|
|
||||||
module ActionMailer #:nodoc:
|
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
|
# = 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
|
# $ script/generate mailer Notifier
|
||||||
#
|
#
|
||||||
# The generated model inherits from ActionMailer::Base. Emails are defined by creating methods within the model which are then
|
# The generated model inherits from ActionMailer::Base. Emails are defined by creating methods within the model which are then
|
||||||
# used to set variables to be used in the mail template, to change options on the mail, or
|
# used to set variables to be used in the mail template, to change options on the mail, or
|
||||||
# to add attachments.
|
# to add attachments.
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
|
@ -35,25 +35,30 @@ module ActionMailer #:nodoc:
|
||||||
# * <tt>subject</tt> - The subject of your email. Sets the <tt>Subject:</tt> header.
|
# * <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>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>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>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>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>.
|
# * <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
|
# 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.
|
# 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
|
# in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
|
||||||
# view.
|
# view.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# = Mailer views
|
# = 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.
|
# 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
|
# 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
|
# in your mailer model. For example, in the mailer defined above, the template at
|
||||||
# <tt>app/views/notifier/signup_notification.erb</tt> would be used to generate the email.
|
# <tt>app/views/notifier/signup_notification.erb</tt> would be used to generate the email.
|
||||||
#
|
#
|
||||||
# Variables defined in the model are accessible as instance variables in the view.
|
# Variables defined in the model are accessible as instance variables in the view.
|
||||||
|
@ -67,33 +72,48 @@ module ActionMailer #:nodoc:
|
||||||
#
|
#
|
||||||
# You got a new note!
|
# You got a new note!
|
||||||
# <%= truncate(note.body, 25) %>
|
# <%= truncate(note.body, 25) %>
|
||||||
#
|
|
||||||
#
|
#
|
||||||
# = Generating URLs for mailer views
|
|
||||||
#
|
#
|
||||||
# If your view includes URLs from the application, you need to use url_for in the mailing method instead of the view.
|
# = Generating URLs
|
||||||
# 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:
|
|
||||||
#
|
#
|
||||||
# def signup_notification(recipient)
|
# URLs can be generated in mailer views using <tt>url_for</tt> or named routes.
|
||||||
# recipients recipient.email_address_with_name
|
# Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request,
|
||||||
# from "system@example.com"
|
# so you'll need to provide all of the details needed to generate a URL.
|
||||||
# subject "New account information"
|
|
||||||
# body :account => recipient,
|
|
||||||
# :home_page => url_for(:host => "example.com", :controller => "welcome", :action => "greeting")
|
|
||||||
# end
|
|
||||||
#
|
#
|
||||||
# You can now access @home_page in the template and get http://example.com/welcome/greeting.
|
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
|
||||||
|
#
|
||||||
|
# <%= 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
|
# = Sending mail
|
||||||
#
|
#
|
||||||
# Once a mailer action and template are defined, you can deliver your message or create it and save it
|
# Once a mailer action and template are defined, you can deliver your message or create it and save it
|
||||||
# for delivery later:
|
# for delivery later:
|
||||||
#
|
#
|
||||||
# Notifier.deliver_signup_notification(david) # sends the email
|
# Notifier.deliver_signup_notification(david) # sends the email
|
||||||
# mail = Notifier.create_signup_notification(david) # => a tmail object
|
# mail = Notifier.create_signup_notification(david) # => a tmail object
|
||||||
# Notifier.deliver(mail)
|
# Notifier.deliver(mail)
|
||||||
#
|
#
|
||||||
# You never instantiate your mailer class. Rather, your delivery instance
|
# You never instantiate your mailer class. Rather, your delivery instance
|
||||||
# methods are automatically wrapped in class methods that start with the word
|
# methods are automatically wrapped in class methods that start with the word
|
||||||
# <tt>deliver_</tt> followed by the name of the mailer method that you would
|
# <tt>deliver_</tt> followed by the name of the mailer method that you would
|
||||||
|
@ -108,13 +128,13 @@ module ActionMailer #:nodoc:
|
||||||
#
|
#
|
||||||
# class MyMailer < ActionMailer::Base
|
# class MyMailer < ActionMailer::Base
|
||||||
# def signup_notification(recipient)
|
# def signup_notification(recipient)
|
||||||
# recipients recipient.email_address_with_name
|
# recipients recipient.email_address_with_name
|
||||||
# subject "New account information"
|
# subject "New account information"
|
||||||
# body "account" => recipient
|
# from "system@example.com"
|
||||||
# from "system@example.com"
|
# body :account => recipient
|
||||||
# content_type "text/html" # Here's where the magic happens
|
# content_type "text/html"
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# = Multipart email
|
# = Multipart email
|
||||||
|
@ -126,6 +146,7 @@ module ActionMailer #:nodoc:
|
||||||
# recipients recipient.email_address_with_name
|
# recipients recipient.email_address_with_name
|
||||||
# subject "New account information"
|
# subject "New account information"
|
||||||
# from "system@example.com"
|
# from "system@example.com"
|
||||||
|
# content_type "multipart/alternative"
|
||||||
#
|
#
|
||||||
# part :content_type => "text/html",
|
# part :content_type => "text/html",
|
||||||
# :body => render_message("signup-as-html", :account => recipient)
|
# :body => render_message("signup-as-html", :account => recipient)
|
||||||
|
@ -136,21 +157,26 @@ module ActionMailer #:nodoc:
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
# 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
|
# 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.
|
# by the content type. Each such detected template will be added as separate part to the message.
|
||||||
#
|
#
|
||||||
# For example, if the following templates existed:
|
# For example, if the following templates existed:
|
||||||
# * signup_notification.text.plain.erb
|
# * signup_notification.text.plain.erb
|
||||||
# * signup_notification.text.html.erb
|
# * signup_notification.text.html.erb
|
||||||
# * signup_notification.text.xml.builder
|
# * signup_notification.text.xml.builder
|
||||||
# * signup_notification.text.x-yaml.erb
|
# * 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.
|
|
||||||
#
|
#
|
||||||
|
# Each would be rendered and added as a separate part to the message,
|
||||||
|
# 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
|
# = Attachments
|
||||||
#
|
#
|
||||||
|
@ -172,51 +198,52 @@ module ActionMailer #:nodoc:
|
||||||
# a.body = generate_your_pdf_here()
|
# a.body = generate_your_pdf_here()
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# = Configuration options
|
# = Configuration options
|
||||||
#
|
#
|
||||||
# These options are specified on the class level, like <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
|
# 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.
|
# * <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.
|
# 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>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>: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>: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>: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>: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>: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.
|
# * <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
|
# 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>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 "/usr/sbin/sendmail"
|
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
|
||||||
# * <tt>:arguments</tt> The command line arguments
|
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt>.
|
||||||
# * <tt>raise_delivery_errors</tt> - whether or not errors should be raised if the email fails to be delivered.
|
|
||||||
#
|
#
|
||||||
# * <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.
|
# 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.
|
# 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
|
# * <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
|
# * <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>.
|
# 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 "1.0". You
|
# * <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 <tt>@mime_version</tt>.
|
# 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
|
# * <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
|
# 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
|
# 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
|
class Base
|
||||||
include AdvAttrAccessor, PartContainer
|
include AdvAttrAccessor, PartContainer
|
||||||
include ActionController::UrlWriter if Object.const_defined?(:ActionController)
|
include ActionController::UrlWriter if Object.const_defined?(:ActionController)
|
||||||
|
@ -229,16 +256,16 @@ module ActionMailer #:nodoc:
|
||||||
cattr_accessor :template_extensions
|
cattr_accessor :template_extensions
|
||||||
@@template_extensions = ['erb', 'builder', 'rhtml', 'rxml']
|
@@template_extensions = ['erb', 'builder', 'rhtml', 'rxml']
|
||||||
|
|
||||||
@@smtp_settings = {
|
@@smtp_settings = {
|
||||||
:address => "localhost",
|
:address => "localhost",
|
||||||
:port => 25,
|
:port => 25,
|
||||||
:domain => 'localhost.localdomain',
|
:domain => 'localhost.localdomain',
|
||||||
:user_name => nil,
|
:user_name => nil,
|
||||||
:password => nil,
|
:password => nil,
|
||||||
:authentication => nil
|
:authentication => nil
|
||||||
}
|
}
|
||||||
cattr_accessor :smtp_settings
|
cattr_accessor :smtp_settings
|
||||||
|
|
||||||
@@sendmail_settings = {
|
@@sendmail_settings = {
|
||||||
:location => '/usr/sbin/sendmail',
|
:location => '/usr/sbin/sendmail',
|
||||||
:arguments => '-i -t'
|
:arguments => '-i -t'
|
||||||
|
@ -250,10 +277,10 @@ module ActionMailer #:nodoc:
|
||||||
|
|
||||||
superclass_delegating_accessor :delivery_method
|
superclass_delegating_accessor :delivery_method
|
||||||
self.delivery_method = :smtp
|
self.delivery_method = :smtp
|
||||||
|
|
||||||
@@perform_deliveries = true
|
@@perform_deliveries = true
|
||||||
cattr_accessor :perform_deliveries
|
cattr_accessor :perform_deliveries
|
||||||
|
|
||||||
@@deliveries = []
|
@@deliveries = []
|
||||||
cattr_accessor :deliveries
|
cattr_accessor :deliveries
|
||||||
|
|
||||||
|
@ -262,7 +289,7 @@ module ActionMailer #:nodoc:
|
||||||
|
|
||||||
@@default_content_type = "text/plain"
|
@@default_content_type = "text/plain"
|
||||||
cattr_accessor :default_content_type
|
cattr_accessor :default_content_type
|
||||||
|
|
||||||
@@default_mime_version = "1.0"
|
@@default_mime_version = "1.0"
|
||||||
cattr_accessor :default_mime_version
|
cattr_accessor :default_mime_version
|
||||||
|
|
||||||
|
@ -271,47 +298,51 @@ module ActionMailer #:nodoc:
|
||||||
|
|
||||||
# Specify the BCC addresses for the message
|
# Specify the BCC addresses for the message
|
||||||
adv_attr_accessor :bcc
|
adv_attr_accessor :bcc
|
||||||
|
|
||||||
# Define the body of the message. This is either a Hash (in which case it
|
# Define the body of the message. This is either a Hash (in which case it
|
||||||
# specifies the variables to pass to the template when it is rendered),
|
# specifies the variables to pass to the template when it is rendered),
|
||||||
# or a string, in which case it specifies the actual text of the message.
|
# or a string, in which case it specifies the actual text of the message.
|
||||||
adv_attr_accessor :body
|
adv_attr_accessor :body
|
||||||
|
|
||||||
# Specify the CC addresses for the message.
|
# Specify the CC addresses for the message.
|
||||||
adv_attr_accessor :cc
|
adv_attr_accessor :cc
|
||||||
|
|
||||||
# Specify the charset to use for the message. This defaults to the
|
# Specify the charset to use for the message. This defaults to the
|
||||||
# +default_charset+ specified for ActionMailer::Base.
|
# +default_charset+ specified for ActionMailer::Base.
|
||||||
adv_attr_accessor :charset
|
adv_attr_accessor :charset
|
||||||
|
|
||||||
# Specify the content type for the message. This defaults to <tt>text/plain</tt>
|
# Specify the content type for the message. This defaults to <tt>text/plain</tt>
|
||||||
# in most cases, but can be automatically set in some situations.
|
# in most cases, but can be automatically set in some situations.
|
||||||
adv_attr_accessor :content_type
|
adv_attr_accessor :content_type
|
||||||
|
|
||||||
# Specify the from address for the message.
|
# Specify the from address for the message.
|
||||||
adv_attr_accessor :from
|
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.
|
# Specify additional headers to be added to the message.
|
||||||
adv_attr_accessor :headers
|
adv_attr_accessor :headers
|
||||||
|
|
||||||
# Specify the order in which parts should be sorted, based on content-type.
|
# Specify the order in which parts should be sorted, based on content-type.
|
||||||
# This defaults to the value for the +default_implicit_parts_order+.
|
# This defaults to the value for the +default_implicit_parts_order+.
|
||||||
adv_attr_accessor :implicit_parts_order
|
adv_attr_accessor :implicit_parts_order
|
||||||
|
|
||||||
# Defaults to "1.0", but may be explicitly given if needed.
|
# Defaults to "1.0", but may be explicitly given if needed.
|
||||||
adv_attr_accessor :mime_version
|
adv_attr_accessor :mime_version
|
||||||
|
|
||||||
# The recipient addresses for the message, either as a string (for a single
|
# The recipient addresses for the message, either as a string (for a single
|
||||||
# address) or an array (for multiple addresses).
|
# address) or an array (for multiple addresses).
|
||||||
adv_attr_accessor :recipients
|
adv_attr_accessor :recipients
|
||||||
|
|
||||||
# The date on which the message was sent. If not set (the default), the
|
# The date on which the message was sent. If not set (the default), the
|
||||||
# header will be set by the delivery agent.
|
# header will be set by the delivery agent.
|
||||||
adv_attr_accessor :sent_on
|
adv_attr_accessor :sent_on
|
||||||
|
|
||||||
# Specify the subject of the message.
|
# Specify the subject of the message.
|
||||||
adv_attr_accessor :subject
|
adv_attr_accessor :subject
|
||||||
|
|
||||||
# Specify the template name to use for current message. This is the "base"
|
# Specify the template name to use for current message. This is the "base"
|
||||||
# template name, without the extension or directory, and may be used to
|
# template name, without the extension or directory, and may be used to
|
||||||
# have multiple mailer methods share the same template.
|
# have multiple mailer methods share the same template.
|
||||||
|
@ -327,7 +358,7 @@ module ActionMailer #:nodoc:
|
||||||
self.class.mailer_name
|
self.class.mailer_name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mailer_name=(value)
|
def mailer_name=(value)
|
||||||
self.class.mailer_name = value
|
self.class.mailer_name = value
|
||||||
end
|
end
|
||||||
|
@ -357,8 +388,8 @@ module ActionMailer #:nodoc:
|
||||||
|
|
||||||
# Receives a raw email, parses it into an email object, decodes it,
|
# Receives a raw email, parses it into an email object, decodes it,
|
||||||
# instantiates a new mailer, and passes the email object to the mailer
|
# 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
|
# object's +receive+ method. If you want your mailer to be able to
|
||||||
# process incoming messages, you'll need to implement a #receive
|
# process incoming messages, you'll need to implement a +receive+
|
||||||
# method that accepts the email object as a parameter:
|
# method that accepts the email object as a parameter:
|
||||||
#
|
#
|
||||||
# class MyMailer < ActionMailer::Base
|
# class MyMailer < ActionMailer::Base
|
||||||
|
@ -387,12 +418,17 @@ module ActionMailer #:nodoc:
|
||||||
# templating language other than rhtml or rxml are supported.
|
# templating language other than rhtml or rxml are supported.
|
||||||
# To use this, include in your template-language plugin's init
|
# To use this, include in your template-language plugin's init
|
||||||
# code or on a per-application basis, this can be invoked from
|
# 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')
|
# ActionMailer::Base.register_template_extension('haml')
|
||||||
def register_template_extension(extension)
|
def register_template_extension(extension)
|
||||||
template_extensions << extension
|
template_extensions << extension
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def template_root=(root)
|
||||||
|
write_inheritable_attribute(:template_root, root)
|
||||||
|
ActionView::TemplateFinder.process_view_paths(root)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
|
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
|
||||||
|
@ -400,7 +436,7 @@ module ActionMailer #:nodoc:
|
||||||
# remain uninitialized (useful when you only need to invoke the "receive"
|
# remain uninitialized (useful when you only need to invoke the "receive"
|
||||||
# method, for instance).
|
# method, for instance).
|
||||||
def initialize(method_name=nil, *parameters) #:nodoc:
|
def initialize(method_name=nil, *parameters) #:nodoc:
|
||||||
create!(method_name, *parameters) if method_name
|
create!(method_name, *parameters) if method_name
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize the mailer via the given +method_name+. The body will be
|
# Initialize the mailer via the given +method_name+. The body will be
|
||||||
|
@ -459,11 +495,14 @@ module ActionMailer #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
# Delivers a TMail::Mail object. By default, it delivers the cached mail
|
# 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.
|
# no alternate has been given as the parameter, this will fail.
|
||||||
def deliver!(mail = @mail)
|
def deliver!(mail = @mail)
|
||||||
raise "no mail object available for delivery!" unless 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
|
begin
|
||||||
__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
|
__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
|
||||||
|
@ -483,7 +522,7 @@ module ActionMailer #:nodoc:
|
||||||
@content_type ||= @@default_content_type.dup
|
@content_type ||= @@default_content_type.dup
|
||||||
@implicit_parts_order ||= @@default_implicit_parts_order.dup
|
@implicit_parts_order ||= @@default_implicit_parts_order.dup
|
||||||
@template ||= method_name
|
@template ||= method_name
|
||||||
@mailer_name ||= Inflector.underscore(self.class.name)
|
@mailer_name ||= self.class.name.underscore
|
||||||
@parts ||= []
|
@parts ||= []
|
||||||
@headers ||= {}
|
@headers ||= {}
|
||||||
@body ||= {}
|
@body ||= {}
|
||||||
|
@ -542,13 +581,14 @@ module ActionMailer #:nodoc:
|
||||||
def create_mail
|
def create_mail
|
||||||
m = TMail::Mail.new
|
m = TMail::Mail.new
|
||||||
|
|
||||||
m.subject, = quote_any_if_necessary(charset, subject)
|
m.subject, = quote_any_if_necessary(charset, subject)
|
||||||
m.to, m.from = quote_any_address_if_necessary(charset, recipients, from)
|
m.to, m.from = quote_any_address_if_necessary(charset, recipients, from)
|
||||||
m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil?
|
m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil?
|
||||||
m.cc = quote_address_if_necessary(cc, charset) unless cc.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.mime_version = mime_version unless mime_version.nil?
|
||||||
m.date = sent_on.to_time rescue sent_on if sent_on
|
m.date = sent_on.to_time rescue sent_on if sent_on
|
||||||
|
|
||||||
headers.each { |k, v| m[k] = v }
|
headers.each { |k, v| m[k] = v }
|
||||||
|
|
||||||
real_content_type, ctype_attrs = parse_content_type
|
real_content_type, ctype_attrs = parse_content_type
|
||||||
|
@ -569,7 +609,7 @@ module ActionMailer #:nodoc:
|
||||||
part = (TMail::Mail === p ? p : p.to_mail(self))
|
part = (TMail::Mail === p ? p : p.to_mail(self))
|
||||||
m.parts << part
|
m.parts << part
|
||||||
end
|
end
|
||||||
|
|
||||||
if real_content_type =~ /multipart/
|
if real_content_type =~ /multipart/
|
||||||
ctype_attrs.delete "charset"
|
ctype_attrs.delete "charset"
|
||||||
m.set_content_type(real_content_type, nil, ctype_attrs)
|
m.set_content_type(real_content_type, nil, ctype_attrs)
|
||||||
|
@ -582,15 +622,18 @@ module ActionMailer #:nodoc:
|
||||||
def perform_delivery_smtp(mail)
|
def perform_delivery_smtp(mail)
|
||||||
destinations = mail.destinations
|
destinations = mail.destinations
|
||||||
mail.ready_to_send
|
mail.ready_to_send
|
||||||
|
sender = mail['return-path'] || mail.from
|
||||||
|
|
||||||
Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain],
|
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_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
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform_delivery_sendmail(mail)
|
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.print(mail.encoded.gsub(/\r/, ''))
|
||||||
sm.flush
|
sm.flush
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,7 +22,7 @@ module ActionMailer
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
|
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
|
||||||
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
|
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
|
||||||
# available to the templates.
|
# available to the templates.
|
||||||
def add_template_helper(helper_module) #:nodoc:
|
def add_template_helper(helper_module) #:nodoc:
|
||||||
master_helper_module.module_eval "include #{helper_module}"
|
master_helper_module.module_eval "include #{helper_module}"
|
||||||
|
@ -34,7 +34,7 @@ module ActionMailer
|
||||||
# helper FooHelper
|
# helper FooHelper
|
||||||
# includes FooHelper in the template class.
|
# includes FooHelper in the template class.
|
||||||
# helper { def foo() "#{bar} is the very best" end }
|
# 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 }
|
# helper(:three, BlindHelper) { def mice() 'mice' end }
|
||||||
# does all three.
|
# does all three.
|
||||||
def helper(*args, &block)
|
def helper(*args, &block)
|
||||||
|
@ -45,7 +45,7 @@ module ActionMailer
|
||||||
when String, Symbol
|
when String, Symbol
|
||||||
file_name = arg.to_s.underscore + '_helper'
|
file_name = arg.to_s.underscore + '_helper'
|
||||||
class_name = file_name.camelize
|
class_name = file_name.camelize
|
||||||
|
|
||||||
begin
|
begin
|
||||||
require_dependency(file_name)
|
require_dependency(file_name)
|
||||||
rescue LoadError => load_error
|
rescue LoadError => load_error
|
||||||
|
@ -87,17 +87,17 @@ module ActionMailer
|
||||||
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def inherited_with_helper(child)
|
def inherited_with_helper(child)
|
||||||
inherited_without_helper(child)
|
inherited_without_helper(child)
|
||||||
begin
|
begin
|
||||||
child.master_helper_module = Module.new
|
child.master_helper_module = Module.new
|
||||||
child.master_helper_module.send! :include, master_helper_module
|
child.master_helper_module.send! :include, master_helper_module
|
||||||
child.helper child.name.underscore
|
child.helper child.name.to_s.underscore
|
||||||
rescue MissingSourceFile => e
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -5,7 +5,7 @@ require 'action_mailer/utils'
|
||||||
module ActionMailer
|
module ActionMailer
|
||||||
# Represents a subpart of an email message. It shares many similar
|
# Represents a subpart of an email message. It shares many similar
|
||||||
# attributes of ActionMailer::Base. Although you can create parts manually
|
# 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.
|
# to use the helper methods in ActionMailer::PartContainer.
|
||||||
class Part
|
class Part
|
||||||
include ActionMailer::AdvAttrAccessor
|
include ActionMailer::AdvAttrAccessor
|
||||||
|
@ -13,7 +13,7 @@ module ActionMailer
|
||||||
|
|
||||||
# Represents the body of the part, as a string. This should not be a
|
# 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
|
# 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.
|
# and assign the result here.
|
||||||
adv_attr_accessor :body
|
adv_attr_accessor :body
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ module ActionMailer
|
||||||
|
|
||||||
# Quote the given text if it contains any "illegal" characters
|
# Quote the given text if it contains any "illegal" characters
|
||||||
def quote_if_necessary(text, charset)
|
def quote_if_necessary(text, charset)
|
||||||
|
text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
|
||||||
|
|
||||||
(text =~ CHARS_NEEDING_QUOTING) ?
|
(text =~ CHARS_NEEDING_QUOTING) ?
|
||||||
quoted_printable(text, charset) :
|
quoted_printable(text, charset) :
|
||||||
text
|
text
|
||||||
|
@ -38,7 +40,7 @@ module ActionMailer
|
||||||
# regular email address, or it can be a phrase followed by an address in
|
# 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
|
# 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
|
# 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)
|
def quote_address_if_necessary(address, charset)
|
||||||
if Array === address
|
if Array === address
|
||||||
address.map { |a| quote_address_if_necessary(a, charset) }
|
address.map { |a| quote_address_if_necessary(a, charset) }
|
||||||
|
|
|
@ -8,11 +8,13 @@ module ActionMailer
|
||||||
"test case definition"
|
"test case definition"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# New Test Super class for forward compatibility.
|
|
||||||
# To override
|
|
||||||
class TestCase < ActiveSupport::TestCase
|
class TestCase < ActiveSupport::TestCase
|
||||||
include ActionMailer::Quoting
|
include ActionMailer::Quoting
|
||||||
|
|
||||||
|
setup :initialize_test_deliveries
|
||||||
|
setup :set_expected_mail
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def tests(mailer)
|
def tests(mailer)
|
||||||
write_inheritable_attribute(:mailer_class, mailer)
|
write_inheritable_attribute(:mailer_class, mailer)
|
||||||
|
@ -33,15 +35,18 @@ module ActionMailer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup
|
protected
|
||||||
ActionMailer::Base.delivery_method = :test
|
def initialize_test_deliveries
|
||||||
ActionMailer::Base.perform_deliveries = true
|
ActionMailer::Base.delivery_method = :test
|
||||||
ActionMailer::Base.deliveries = []
|
ActionMailer::Base.perform_deliveries = true
|
||||||
|
ActionMailer::Base.deliveries = []
|
||||||
|
end
|
||||||
|
|
||||||
@expected = TMail::Mail.new
|
def set_expected_mail
|
||||||
@expected.set_content_type "text", "plain", { "charset" => charset }
|
@expected = TMail::Mail.new
|
||||||
@expected.mime_version = '1.0'
|
@expected.set_content_type "text", "plain", { "charset" => charset }
|
||||||
end
|
@expected.mime_version = '1.0'
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def charset
|
def charset
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
|
|
||||||
begin
|
begin
|
||||||
gem 'tmail', '~> 1.1.0'
|
gem 'tmail', '~> 1.2.3'
|
||||||
rescue Gem::LoadError
|
rescue Gem::LoadError
|
||||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.1.0"
|
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.2.3"
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
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