mirror of
https://github.com/TracksApp/tracks.git
synced 2025-09-22 05:50:47 +02:00
Upgraded to Rails 2.1. This can have wide ranging consequences, so please help track down any issues introduced by the upgrade. Requires environment.rb modifications.
Changes you will need to make: * In your environment.rb, you will need to update references to a few files per environment.rb.tmpl * In your environment.rb, you will need to specify the local time zone of the computer that is running your Tracks install. Other notes on my changes: * Modified our code to take advantage of Rails 2.1's slick time zone support. * Upgraded will_paginate for compatibility * Hacked the Selenium on Rails plugin, which has not been updated in some time and does not support Rails 2.1 * Verified that all tests pass on my machine, including Selenium tests -- I'd like confirmation from others, too.
This commit is contained in:
parent
f3bae73868
commit
901a58f8a3
1086 changed files with 51452 additions and 19526 deletions
|
@ -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}
|
||||||
|
|
|
@ -267,9 +267,9 @@ class TodosController < ApplicationController
|
||||||
def completed
|
def completed
|
||||||
@page_title = "TRACKS::Completed tasks"
|
@page_title = "TRACKS::Completed tasks"
|
||||||
@done = current_user.completed_todos
|
@done = current_user.completed_todos
|
||||||
@done_today = @done.completed_within current_user.time - 1.day
|
@done_today = @done.completed_within Time.zone.now - 1.day
|
||||||
@done_this_week = @done.completed_within current_user.time - 1.week
|
@done_this_week = @done.completed_within Time.zone.now - 1.week
|
||||||
@done_this_month = @done.completed_within current_user.time - 4.week
|
@done_this_month = @done.completed_within Time.zone.now - 4.week
|
||||||
@count = @done_today.size + @done_this_week.size + @done_this_month.size
|
@count = @done_today.size + @done_this_week.size + @done_this_month.size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -277,7 +277,7 @@ class TodosController < ApplicationController
|
||||||
@page_title = "TRACKS::Archived completed tasks"
|
@page_title = "TRACKS::Archived completed tasks"
|
||||||
@done = current_user.completed_todos
|
@done = current_user.completed_todos
|
||||||
@count = @done.size
|
@count = @done.size
|
||||||
@done_archive = @done.completed_more_than current_user.time - 28.days
|
@done_archive = @done.completed_more_than Time.zone.now - 28.days
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_deferred
|
def list_deferred
|
||||||
|
@ -392,7 +392,7 @@ class TodosController < ApplicationController
|
||||||
|
|
||||||
if params.key?('due')
|
if params.key?('due')
|
||||||
due_within = params['due'].to_i
|
due_within = params['due'].to_i
|
||||||
due_within_when = current_user.time + due_within.days
|
due_within_when = Time.zone.now + due_within.days
|
||||||
condition_builder.add('todos.due <= ?', due_within_when)
|
condition_builder.add('todos.due <= ?', due_within_when)
|
||||||
due_within_date_s = due_within_when.strftime("%Y-%m-%d")
|
due_within_date_s = due_within_when.strftime("%Y-%m-%d")
|
||||||
@title << " due today" if (due_within == 0)
|
@title << " due today" if (due_within == 0)
|
||||||
|
@ -402,7 +402,7 @@ class TodosController < ApplicationController
|
||||||
|
|
||||||
if params.key?('done')
|
if params.key?('done')
|
||||||
done_in_last = params['done'].to_i
|
done_in_last = params['done'].to_i
|
||||||
condition_builder.add('todos.completed_at >= ?', current_user.time - done_in_last.days)
|
condition_builder.add('todos.completed_at >= ?', Time.zone.now - done_in_last.days)
|
||||||
@title << " actions completed"
|
@title << " actions completed"
|
||||||
@description << " in the last #{done_in_last.to_s} days"
|
@description << " in the last #{done_in_last.to_s} days"
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
@ -57,7 +57,11 @@ ActionController::Routing::Routes.draw do |map|
|
||||||
|
|
||||||
map.preferences 'preferences', :controller => 'preferences', :action => 'index'
|
map.preferences 'preferences', :controller => 'preferences', :action => 'index'
|
||||||
map.integrations 'integrations', :controller => 'integrations', :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.
|
# Install the default route as the lowest priority.
|
||||||
map.connect ':controller/:action/:id'
|
map.connect ':controller/:action/:id'
|
||||||
|
|
||||||
|
|
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"
|
||||||
|
|
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
|
||||||
|
|
|
@ -245,7 +245,7 @@ class TodosControllerTest < Test::Rails::TestCase
|
||||||
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
2
vendor/plugins/selenium-on-rails/init.rb
vendored
2
vendor/plugins/selenium-on-rails/init.rb
vendored
|
@ -7,6 +7,8 @@ if envs.include? RAILS_ENV
|
||||||
require 'selenium_controller'
|
require 'selenium_controller'
|
||||||
require File.dirname(__FILE__) + '/routes'
|
require File.dirname(__FILE__) + '/routes'
|
||||||
|
|
||||||
|
SeleniumController.prepend_view_path File.expand_path(File.dirname(__FILE__) + '/lib/views')
|
||||||
|
|
||||||
else
|
else
|
||||||
#erase all traces
|
#erase all traces
|
||||||
$LOAD_PATH.delete lib_path
|
$LOAD_PATH.delete lib_path
|
||||||
|
|
|
@ -13,13 +13,8 @@ module SeleniumOnRails
|
||||||
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
|
|
||||||
# to the app/views/ directory since Rails doesn't support absolute paths
|
|
||||||
# to layout templates.
|
|
||||||
def layout_path
|
def layout_path
|
||||||
rails_root = Pathname.new File.expand_path(File.join(RAILS_ROOT, 'app/views'))
|
'/layout.rhtml'
|
||||||
view_path = Pathname.new view_path('layout')
|
|
||||||
view_path.relative_path_from(rails_root).to_s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fixtures_path
|
def fixtures_path
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
# 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
|
||||||
|
@ -20,16 +20,16 @@ class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
|
||||||
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 = ''
|
|
||||||
local_assigns.each_key {|key| assign_locals_code << "#{key} = local_assigns[#{key.inspect}];"}
|
|
||||||
|
|
||||||
eval assign_locals_code + "\n" + template
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compilable?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
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
|
||||||
|
@ -8,8 +8,8 @@ class SeleniumOnRails::Selenese
|
||||||
@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)
|
||||||
|
@ -18,6 +18,10 @@ class SeleniumOnRails::Selenese
|
||||||
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
|
||||||
|
|
||||||
|
def compilable?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def next_line lines, expects
|
def next_line lines, expects
|
||||||
|
|
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
|
|
|
@ -1,381 +0,0 @@
|
||||||
#
|
|
||||||
# parser.y
|
|
||||||
#
|
|
||||||
# Copyright (c) 1998-2007 Minero Aoki
|
|
||||||
#
|
|
||||||
# This program is free software.
|
|
||||||
# You can distribute/modify this program under the terms of
|
|
||||||
# the GNU Lesser General Public License version 2.1.
|
|
||||||
#
|
|
||||||
|
|
||||||
class TMail::Parser
|
|
||||||
|
|
||||||
options no_result_var
|
|
||||||
|
|
||||||
rule
|
|
||||||
|
|
||||||
content : DATETIME datetime { val[1] }
|
|
||||||
| RECEIVED received { val[1] }
|
|
||||||
| MADDRESS addrs_TOP { val[1] }
|
|
||||||
| RETPATH retpath { val[1] }
|
|
||||||
| KEYWORDS keys { val[1] }
|
|
||||||
| ENCRYPTED enc { val[1] }
|
|
||||||
| MIMEVERSION version { val[1] }
|
|
||||||
| CTYPE ctype { val[1] }
|
|
||||||
| CENCODING cencode { val[1] }
|
|
||||||
| CDISPOSITION cdisp { val[1] }
|
|
||||||
| ADDRESS addr_TOP { val[1] }
|
|
||||||
| MAILBOX mbox { val[1] }
|
|
||||||
|
|
||||||
datetime : day DIGIT ATOM DIGIT hour zone
|
|
||||||
# 0 1 2 3 4 5
|
|
||||||
# date month year
|
|
||||||
{
|
|
||||||
t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0)
|
|
||||||
(t + val[4] - val[5]).localtime
|
|
||||||
}
|
|
||||||
|
|
||||||
day : /* none */
|
|
||||||
| ATOM ','
|
|
||||||
|
|
||||||
hour : DIGIT ':' DIGIT
|
|
||||||
{
|
|
||||||
(val[0].to_i * 60 * 60) +
|
|
||||||
(val[2].to_i * 60)
|
|
||||||
}
|
|
||||||
| DIGIT ':' DIGIT ':' DIGIT
|
|
||||||
{
|
|
||||||
(val[0].to_i * 60 * 60) +
|
|
||||||
(val[2].to_i * 60) +
|
|
||||||
(val[4].to_i)
|
|
||||||
}
|
|
||||||
|
|
||||||
zone : ATOM
|
|
||||||
{
|
|
||||||
timezone_string_to_unixtime(val[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
received : from by via with id for received_datetime
|
|
||||||
{
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
from : /* none */
|
|
||||||
| FROM received_domain
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
by : /* none */
|
|
||||||
| BY received_domain
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
received_domain
|
|
||||||
: domain
|
|
||||||
{
|
|
||||||
join_domain(val[0])
|
|
||||||
}
|
|
||||||
| domain '@' domain
|
|
||||||
{
|
|
||||||
join_domain(val[2])
|
|
||||||
}
|
|
||||||
| domain DOMLIT
|
|
||||||
{
|
|
||||||
join_domain(val[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
via : /* none */
|
|
||||||
| VIA ATOM
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
with : /* none */
|
|
||||||
{
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
| with WITH ATOM
|
|
||||||
{
|
|
||||||
val[0].push val[2]
|
|
||||||
val[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
id : /* none */
|
|
||||||
| ID msgid
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
| ID ATOM
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
for : /* none */
|
|
||||||
| FOR received_addrspec
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
received_addrspec
|
|
||||||
: routeaddr
|
|
||||||
{
|
|
||||||
val[0].spec
|
|
||||||
}
|
|
||||||
| spec
|
|
||||||
{
|
|
||||||
val[0].spec
|
|
||||||
}
|
|
||||||
|
|
||||||
received_datetime
|
|
||||||
: /* none */
|
|
||||||
| ';' datetime
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
addrs_TOP : addrs
|
|
||||||
| group_bare
|
|
||||||
| addrs commas group_bare
|
|
||||||
|
|
||||||
addr_TOP : mbox
|
|
||||||
| group
|
|
||||||
| group_bare
|
|
||||||
|
|
||||||
retpath : addrs_TOP
|
|
||||||
| '<' '>' { [ Address.new(nil, nil) ] }
|
|
||||||
|
|
||||||
addrs : addr
|
|
||||||
{
|
|
||||||
val
|
|
||||||
}
|
|
||||||
| addrs commas addr
|
|
||||||
{
|
|
||||||
val[0].push val[2]
|
|
||||||
val[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
addr : mbox
|
|
||||||
| group
|
|
||||||
|
|
||||||
mboxes : mbox
|
|
||||||
{
|
|
||||||
val
|
|
||||||
}
|
|
||||||
| mboxes commas mbox
|
|
||||||
{
|
|
||||||
val[0].push val[2]
|
|
||||||
val[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
mbox : spec
|
|
||||||
| routeaddr
|
|
||||||
| addr_phrase routeaddr
|
|
||||||
{
|
|
||||||
val[1].phrase = Decoder.decode(val[0])
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
group : group_bare ';'
|
|
||||||
|
|
||||||
group_bare: addr_phrase ':' mboxes
|
|
||||||
{
|
|
||||||
AddressGroup.new(val[0], val[2])
|
|
||||||
}
|
|
||||||
| addr_phrase ':' { AddressGroup.new(val[0], []) }
|
|
||||||
|
|
||||||
addr_phrase
|
|
||||||
: local_head { val[0].join('.') }
|
|
||||||
| addr_phrase local_head { val[0] << ' ' << val[1].join('.') }
|
|
||||||
|
|
||||||
routeaddr : '<' routes spec '>'
|
|
||||||
{
|
|
||||||
val[2].routes.replace val[1]
|
|
||||||
val[2]
|
|
||||||
}
|
|
||||||
| '<' spec '>'
|
|
||||||
{
|
|
||||||
val[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
routes : at_domains ':'
|
|
||||||
|
|
||||||
at_domains: '@' domain { [ val[1].join('.') ] }
|
|
||||||
| at_domains ',' '@' domain { val[0].push val[3].join('.'); val[0] }
|
|
||||||
|
|
||||||
spec : local '@' domain { Address.new( val[0], val[2] ) }
|
|
||||||
| local { Address.new( val[0], nil ) }
|
|
||||||
|
|
||||||
local: local_head
|
|
||||||
| local_head '.' { val[0].push ''; val[0] }
|
|
||||||
|
|
||||||
local_head: word
|
|
||||||
{ val }
|
|
||||||
| local_head dots word
|
|
||||||
{
|
|
||||||
val[1].times do
|
|
||||||
val[0].push ''
|
|
||||||
end
|
|
||||||
val[0].push val[2]
|
|
||||||
val[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
domain : domword
|
|
||||||
{ val }
|
|
||||||
| domain dots domword
|
|
||||||
{
|
|
||||||
val[1].times do
|
|
||||||
val[0].push ''
|
|
||||||
end
|
|
||||||
val[0].push val[2]
|
|
||||||
val[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
dots : '.' { 0 }
|
|
||||||
| '.' '.' { 1 }
|
|
||||||
|
|
||||||
word : atom
|
|
||||||
| QUOTED
|
|
||||||
| DIGIT
|
|
||||||
|
|
||||||
domword : atom
|
|
||||||
| DOMLIT
|
|
||||||
| DIGIT
|
|
||||||
|
|
||||||
commas : ','
|
|
||||||
| commas ','
|
|
||||||
|
|
||||||
msgid : '<' spec '>'
|
|
||||||
{
|
|
||||||
val[1] = val[1].spec
|
|
||||||
val.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
keys : phrase { val }
|
|
||||||
| keys ',' phrase { val[0].push val[2]; val[0] }
|
|
||||||
|
|
||||||
phrase : word
|
|
||||||
| phrase word { val[0] << ' ' << val[1] }
|
|
||||||
|
|
||||||
enc : word
|
|
||||||
{
|
|
||||||
val.push nil
|
|
||||||
val
|
|
||||||
}
|
|
||||||
| word word
|
|
||||||
{
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
version : DIGIT '.' DIGIT
|
|
||||||
{
|
|
||||||
[ val[0].to_i, val[2].to_i ]
|
|
||||||
}
|
|
||||||
|
|
||||||
ctype : TOKEN '/' TOKEN params opt_semicolon
|
|
||||||
{
|
|
||||||
[ val[0].downcase, val[2].downcase, decode_params(val[3]) ]
|
|
||||||
}
|
|
||||||
| TOKEN params opt_semicolon
|
|
||||||
{
|
|
||||||
[ val[0].downcase, nil, decode_params(val[1]) ]
|
|
||||||
}
|
|
||||||
|
|
||||||
params : /* none */
|
|
||||||
{
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
| params ';' TOKEN '=' QUOTED
|
|
||||||
{
|
|
||||||
val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"')
|
|
||||||
val[0]
|
|
||||||
}
|
|
||||||
| params ';' TOKEN '=' TOKEN
|
|
||||||
{
|
|
||||||
val[0][ val[2].downcase ] = val[4]
|
|
||||||
val[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
cencode : TOKEN
|
|
||||||
{
|
|
||||||
val[0].downcase
|
|
||||||
}
|
|
||||||
|
|
||||||
cdisp : TOKEN params opt_semicolon
|
|
||||||
{
|
|
||||||
[ val[0].downcase, decode_params(val[1]) ]
|
|
||||||
}
|
|
||||||
|
|
||||||
opt_semicolon
|
|
||||||
:
|
|
||||||
| ';'
|
|
||||||
|
|
||||||
atom : ATOM
|
|
||||||
| FROM
|
|
||||||
| BY
|
|
||||||
| VIA
|
|
||||||
| WITH
|
|
||||||
| ID
|
|
||||||
| FOR
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---- header
|
|
||||||
#
|
|
||||||
# parser.rb
|
|
||||||
#
|
|
||||||
# Copyright (c) 1998-2007 Minero Aoki
|
|
||||||
#
|
|
||||||
# This program is free software.
|
|
||||||
# You can distribute/modify this program under the terms of
|
|
||||||
# the GNU Lesser General Public License version 2.1.
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'tmail/scanner'
|
|
||||||
require 'tmail/utils'
|
|
||||||
|
|
||||||
---- inner
|
|
||||||
|
|
||||||
include TextUtils
|
|
||||||
|
|
||||||
def self.parse( ident, str, cmt = nil )
|
|
||||||
new.parse(ident, str, cmt)
|
|
||||||
end
|
|
||||||
|
|
||||||
MAILP_DEBUG = false
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
self.debug = MAILP_DEBUG
|
|
||||||
end
|
|
||||||
|
|
||||||
def debug=( flag )
|
|
||||||
@yydebug = flag && Racc_debug_parser
|
|
||||||
@scanner_debug = flag
|
|
||||||
end
|
|
||||||
|
|
||||||
def debug
|
|
||||||
@yydebug
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse( ident, str, comments = nil )
|
|
||||||
@scanner = Scanner.new(str, ident, comments)
|
|
||||||
@scanner.debug = @scanner_debug
|
|
||||||
@first = [ident, ident]
|
|
||||||
result = yyparse(self, :parse_in)
|
|
||||||
comments.map! {|c| to_kcode(c) } if comments
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def parse_in( &block )
|
|
||||||
yield @first
|
|
||||||
@scanner.scan(&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def on_error( t, val, vstack )
|
|
||||||
raise SyntaxError, "parse error on token #{racc_token2str t}"
|
|
||||||
end
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
require 'tmail'
|
|
1
vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail.rb → vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb
vendored
Executable file → Normal file
1
vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail.rb → vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb
vendored
Executable file → Normal file
|
@ -2,3 +2,4 @@ require 'tmail/version'
|
||||||
require 'tmail/mail'
|
require 'tmail/mail'
|
||||||
require 'tmail/mailbox'
|
require 'tmail/mailbox'
|
||||||
require 'tmail/core_extensions'
|
require 'tmail/core_extensions'
|
||||||
|
require 'tmail/net'
|
426
vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
vendored
Normal file
426
vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb
vendored
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
=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
|
||||||
|
#
|
||||||
|
# Provides a complete handling library for email addresses. Can parse a string of an
|
||||||
|
# address directly or take in preformatted addresses themseleves. Allows you to add
|
||||||
|
# and remove phrases from the front of the address and provides a compare function for
|
||||||
|
# email addresses.
|
||||||
|
#
|
||||||
|
# == Parsing and Handling a Valid Address:
|
||||||
|
#
|
||||||
|
# Just pass the email address in as a string to Address.parse:
|
||||||
|
#
|
||||||
|
# email = TMail::Address.parse('Mikel Lindsaar <mikel@lindsaar.net>)
|
||||||
|
# #=> #<TMail::Address mikel@lindsaar.net>
|
||||||
|
# email.address
|
||||||
|
# #=> "mikel@lindsaar.net"
|
||||||
|
# email.local
|
||||||
|
# #=> "mikel"
|
||||||
|
# email.domain
|
||||||
|
# #=> "lindsaar.net"
|
||||||
|
# email.name # Aliased as phrase as well
|
||||||
|
# #=> "Mikel Lindsaar"
|
||||||
|
#
|
||||||
|
# == Detecting an Invalid Address
|
||||||
|
#
|
||||||
|
# If you want to check the syntactical validity of an email address, just pass it to
|
||||||
|
# Address.parse and catch any SyntaxError:
|
||||||
|
#
|
||||||
|
# begin
|
||||||
|
# TMail::Mail.parse("mikel 2@@@@@ me .com")
|
||||||
|
# rescue TMail::SyntaxError
|
||||||
|
# puts("Invalid Email Address Detected")
|
||||||
|
# else
|
||||||
|
# puts("Address is valid")
|
||||||
|
# end
|
||||||
|
# #=> "Invalid Email Address Detected"
|
||||||
|
class Address
|
||||||
|
|
||||||
|
include TextUtils #:nodoc:
|
||||||
|
|
||||||
|
# Sometimes you need to parse an address, TMail can do it for you and provide you with
|
||||||
|
# a fairly robust method of detecting a valid address.
|
||||||
|
#
|
||||||
|
# Takes in a string, returns a TMail::Address object.
|
||||||
|
#
|
||||||
|
# Raises a TMail::SyntaxError on invalid email format
|
||||||
|
def Address.parse( str )
|
||||||
|
Parser.parse :ADDRESS, special_quote_address(str)
|
||||||
|
end
|
||||||
|
|
||||||
|
def Address.special_quote_address(str) #:nodoc:
|
||||||
|
# Takes a string which is an address and adds quotation marks to special
|
||||||
|
# edge case methods that the RACC parser can not handle.
|
||||||
|
#
|
||||||
|
# Right now just handles two edge cases:
|
||||||
|
#
|
||||||
|
# Full stop as the last character of the display name:
|
||||||
|
# Mikel L. <mikel@me.com>
|
||||||
|
# Returns:
|
||||||
|
# "Mikel L." <mikel@me.com>
|
||||||
|
#
|
||||||
|
# Unquoted @ symbol in the display name:
|
||||||
|
# mikel@me.com <mikel@me.com>
|
||||||
|
# Returns:
|
||||||
|
# "mikel@me.com" <mikel@me.com>
|
||||||
|
#
|
||||||
|
# Any other address not matching these patterns just gets returned as is.
|
||||||
|
case
|
||||||
|
# This handles the missing "" in an older version of Apple Mail.app
|
||||||
|
# around the display name when the display name contains a '@'
|
||||||
|
# like 'mikel@me.com <mikel@me.com>'
|
||||||
|
# Just quotes it to: '"mikel@me.com" <mikel@me.com>'
|
||||||
|
when str =~ /\A([^"].+@.+[^"])\s(<.*?>)\Z/
|
||||||
|
return "\"#{$1}\" #{$2}"
|
||||||
|
# This handles cases where 'Mikel A. <mikel@me.com>' which is a trailing
|
||||||
|
# full stop before the address section. Just quotes it to
|
||||||
|
# '"Mikel A. <mikel@me.com>"
|
||||||
|
when str =~ /\A(.*?\.)\s(<.*?>)\Z/
|
||||||
|
return "\"#{$1}\" #{$2}"
|
||||||
|
else
|
||||||
|
str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def address_group? #:nodoc:
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Address.new(local, domain)
|
||||||
|
#
|
||||||
|
# Accepts:
|
||||||
|
#
|
||||||
|
# * local - Left of the at symbol
|
||||||
|
#
|
||||||
|
# * domain - Array of the domain split at the periods.
|
||||||
|
#
|
||||||
|
# For example:
|
||||||
|
#
|
||||||
|
# Address.new("mikel", ["lindsaar", "net"])
|
||||||
|
# #=> "#<TMail::Address mikel@lindsaar.net>"
|
||||||
|
def initialize( local, domain )
|
||||||
|
if domain
|
||||||
|
domain.each do |s|
|
||||||
|
raise SyntaxError, 'empty word in domain' if s.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is to catch an unquoted "@" symbol in the local part of the
|
||||||
|
# address. Handles addresses like <"@"@me.com> and makes sure they
|
||||||
|
# stay like <"@"@me.com> (previously were becomming <@@me.com>)
|
||||||
|
if local && (local.join == '@' || local.join =~ /\A[^"].*?@.*?[^"]\Z/)
|
||||||
|
@local = "\"#{local.join}\""
|
||||||
|
else
|
||||||
|
@local = local
|
||||||
|
end
|
||||||
|
|
||||||
|
@domain = domain
|
||||||
|
@name = nil
|
||||||
|
@routes = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Provides the name or 'phrase' of the email address.
|
||||||
|
#
|
||||||
|
# For Example:
|
||||||
|
#
|
||||||
|
# email = TMail::Address.parse("Mikel Lindsaar <mikel@lindsaar.net>")
|
||||||
|
# email.name
|
||||||
|
# #=> "Mikel Lindsaar"
|
||||||
|
def name
|
||||||
|
@name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Setter method for the name or phrase of the email
|
||||||
|
#
|
||||||
|
# For Example:
|
||||||
|
#
|
||||||
|
# email = TMail::Address.parse("mikel@lindsaar.net")
|
||||||
|
# email.name
|
||||||
|
# #=> nil
|
||||||
|
# email.name = "Mikel Lindsaar"
|
||||||
|
# email.to_s
|
||||||
|
# #=> "Mikel Lindsaar <mikel@me.com>"
|
||||||
|
def name=( str )
|
||||||
|
@name = str
|
||||||
|
@name = nil if str and str.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
#:stopdoc:
|
||||||
|
alias phrase name
|
||||||
|
alias phrase= name=
|
||||||
|
#:startdoc:
|
||||||
|
|
||||||
|
# This is still here from RFC 822, and is now obsolete per RFC2822 Section 4.
|
||||||
|
#
|
||||||
|
# "When interpreting addresses, the route portion SHOULD be ignored."
|
||||||
|
#
|
||||||
|
# It is still here, so you can access it.
|
||||||
|
#
|
||||||
|
# Routes return the route portion at the front of the email address, if any.
|
||||||
|
#
|
||||||
|
# For Example:
|
||||||
|
# email = TMail::Address.parse( "<@sa,@another:Mikel@me.com>")
|
||||||
|
# => #<TMail::Address Mikel@me.com>
|
||||||
|
# email.to_s
|
||||||
|
# => "<@sa,@another:Mikel@me.com>"
|
||||||
|
# email.routes
|
||||||
|
# => ["sa", "another"]
|
||||||
|
def routes
|
||||||
|
@routes
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect #:nodoc:
|
||||||
|
"#<#{self.class} #{address()}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the local part of the email address
|
||||||
|
#
|
||||||
|
# For Example:
|
||||||
|
#
|
||||||
|
# email = TMail::Address.parse("mikel@lindsaar.net")
|
||||||
|
# email.local
|
||||||
|
# #=> "mikel"
|
||||||
|
def local
|
||||||
|
return nil unless @local
|
||||||
|
return '""' if @local.size == 1 and @local[0].empty?
|
||||||
|
# Check to see if it is an array before trying to map it
|
||||||
|
if @local.respond_to?(:map)
|
||||||
|
@local.map {|i| quote_atom(i) }.join('.')
|
||||||
|
else
|
||||||
|
quote_atom(@local)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the domain part of the email address
|
||||||
|
#
|
||||||
|
# For Example:
|
||||||
|
#
|
||||||
|
# email = TMail::Address.parse("mikel@lindsaar.net")
|
||||||
|
# email.local
|
||||||
|
# #=> "lindsaar.net"
|
||||||
|
def domain
|
||||||
|
return nil unless @domain
|
||||||
|
join_domain(@domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the full specific address itself
|
||||||
|
#
|
||||||
|
# For Example:
|
||||||
|
#
|
||||||
|
# email = TMail::Address.parse("mikel@lindsaar.net")
|
||||||
|
# email.address
|
||||||
|
# #=> "mikel@lindsaar.net"
|
||||||
|
def spec
|
||||||
|
s = self.local
|
||||||
|
d = self.domain
|
||||||
|
if s and d
|
||||||
|
s + '@' + d
|
||||||
|
else
|
||||||
|
s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias address spec
|
||||||
|
|
||||||
|
# Provides == function to the email. Only checks the actual address
|
||||||
|
# and ignores the name/phrase component
|
||||||
|
#
|
||||||
|
# For Example
|
||||||
|
#
|
||||||
|
# addr1 = TMail::Address.parse("My Address <mikel@lindsaar.net>")
|
||||||
|
# #=> "#<TMail::Address mikel@lindsaar.net>"
|
||||||
|
# addr2 = TMail::Address.parse("Another <mikel@lindsaar.net>")
|
||||||
|
# #=> "#<TMail::Address mikel@lindsaar.net>"
|
||||||
|
# addr1 == addr2
|
||||||
|
# #=> true
|
||||||
|
def ==( other )
|
||||||
|
other.respond_to? :spec and self.spec == other.spec
|
||||||
|
end
|
||||||
|
|
||||||
|
alias eql? ==
|
||||||
|
|
||||||
|
# Provides a unique hash value for this record against the local and domain
|
||||||
|
# parts, ignores the name/phrase value
|
||||||
|
#
|
||||||
|
# email = TMail::Address.parse("mikel@lindsaar.net")
|
||||||
|
# email.hash
|
||||||
|
# #=> 18767598
|
||||||
|
def hash
|
||||||
|
@local.hash ^ @domain.hash
|
||||||
|
end
|
||||||
|
|
||||||
|
# Duplicates a TMail::Address object returning the duplicate
|
||||||
|
#
|
||||||
|
# addr1 = TMail::Address.parse("mikel@lindsaar.net")
|
||||||
|
# addr2 = addr1.dup
|
||||||
|
# addr1.id == addr2.id
|
||||||
|
# #=> false
|
||||||
|
def dup
|
||||||
|
obj = self.class.new(@local.dup, @domain.dup)
|
||||||
|
obj.name = @name.dup if @name
|
||||||
|
obj.routes.replace @routes
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
|
||||||
|
include StrategyInterface #:nodoc:
|
||||||
|
|
||||||
|
def accept( strategy, dummy1 = nil, dummy2 = nil ) #:nodoc:
|
||||||
|
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,6 +1,6 @@
|
||||||
=begin rdoc
|
=begin rdoc
|
||||||
|
|
||||||
= Attachment handling class
|
= Attachment handling file
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
|
@ -17,8 +17,7 @@ module TMail
|
||||||
end
|
end
|
||||||
|
|
||||||
def attachment?(part)
|
def attachment?(part)
|
||||||
(part['content-disposition'] && part['content-disposition'].disposition == "attachment") ||
|
part.disposition_is_attachment? || part.content_type_is_text?
|
||||||
part.header['content-type'].main_type != "text"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def attachments
|
def attachments
|
14
vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/base64.rb → vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb
vendored
Executable file → Normal file
14
vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/base64.rb → vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb
vendored
Executable file → Normal file
|
@ -1,9 +1,4 @@
|
||||||
# = TITLE:
|
#--
|
||||||
#
|
|
||||||
# Base64
|
|
||||||
#
|
|
||||||
# = COPYRIGHT:
|
|
||||||
#
|
|
||||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
@ -27,10 +22,9 @@
|
||||||
#
|
#
|
||||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||||
# with permission of Minero Aoki.
|
# with permission of Minero Aoki.
|
||||||
|
#++
|
||||||
#
|
#:stopdoc:
|
||||||
module TMail
|
module TMail
|
||||||
|
|
||||||
module Base64
|
module Base64
|
||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
@ -48,5 +42,5 @@ module TMail
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
#:startdoc:
|
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