Freeze to rails 1.1.5. The only change I needed to make for compatibility was to ApplicationController#init_not_done_counts

git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@306 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2006-08-10 03:22:47 +00:00
parent 45739e12de
commit a36b736c6a
74 changed files with 1064 additions and 535 deletions

View file

@ -90,8 +90,8 @@ class ApplicationController < ActionController::Base
def init_not_done_counts(parents = ['project','context']) def init_not_done_counts(parents = ['project','context'])
parents.each {|parent| parents.each {|parent|
eval("@#{parent}_not_done_counts = Todo.count(:todo, eval("@#{parent}_not_done_counts = Todo.count(:all,
:conditions => ['todos.user_id = ? and todos.type = ? and todos.done = ?', @user.id, \"Immediate\", false], :conditions => ['user_id = ? and type = ? and done = ?', @user.id, \"Immediate\", false],
:group => :#{parent}_id)") :group => :#{parent}_id)")
} }
end end

View file

@ -1,3 +0,0 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../config/boot'
require 'commands/process/spinner'

View file

@ -1,3 +1,22 @@
*1.2.4* (August 8th, 2006)
* Backport of documentation enhancements. [Kevin Clark, Marcel Molina Jr]
* Correct spurious documentation example code which results in a SyntaxError. [Marcel Molina Jr.]
* Mailer template root applies to a class and its subclasses rather than acting globally. #5555 [somekool@gmail.com]
*1.2.3* (June 29th, 2006)
* Depend on Action Pack 1.12.3
*1.2.2* (June 27th, 2006)
* Depend on Action Pack 1.12.2
*1.2.1* (April 6th, 2005) *1.2.1* (April 6th, 2005)
* Be part of Rails 1.1.1 * Be part of Rails 1.1.1

View file

@ -5,20 +5,92 @@ require 'action_mailer/utils'
require 'tmail/net' require 'tmail/net'
module ActionMailer #:nodoc: module ActionMailer #:nodoc:
# Usage: # ActionMailer allows you to send email from your application using a mailer model and views.
# #
# class ApplicationMailer < ActionMailer::Base # = Mailer Models
# # Set up properties # To use ActionMailer, you need to create a mailer model.
# # Properties can also be specified via accessor methods #
# # (i.e. self.subject = "foo") and instance variables (@subject = "foo"). # $ script/generate mailer Notifier
#
# 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
# to add attachments.
#
# Examples:
#
# class Notifier < ActionMailer::Base
# def signup_notification(recipient)
# recipients recipient.email_address_with_name
# from "system@example.com"
# subject "New account information"
# body "account" => recipient
# end
# end
#
# Mailer methods have the following configuration methods available.
#
# * <tt>recipients</tt> - Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the <tt>To:</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>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>sent_on</tt> - The date on which the message was sent. If not set, the header wil be set by the delivery agent.
# * <tt>content_type</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
# * <tt>headers</tt> - Specify additional headers to be set for the message, e.g. <tt>headers 'X-Mail-Count' => 107370</tt>.
#
# The <tt>body</tt> method has special behavior. It takes a hash which generates an instance variable
# named after each key in the hash containing the value that that key points to.
#
# So, for example, <tt>body "account" => recipient</tt> would result
# in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
# view.
#
# = Mailer Views
# Like ActionController, each mailer class has a corresponding view directory
# 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>.rhtml</tt> file with the same name as the method
# in your mailer model. For example, in the mailer defined above, the template at
# <tt>app/views/notifier/signup_notification.rhtml</tt> would be used to generate the email.
#
# Variables defined in the model are accessible as instance variables in the view.
#
# Emails by default are sent in plain text, so a sample view for our model example might look like this:
#
# Hi <%= @account.name %>,
# Thanks for joining our service! Please check back often.
#
# = Sending Mail
# Once a mailer action and template are defined, you can deliver your message or create it and save it
# for delivery later:
#
# Notifier.deliver_signup_notification(david) # sends the email
# mail = Notifier.create_signup_notification(david) # => a tmail object
# Notifier.deliver(mail)
#
# You never instantiate your mailer class. Rather, your delivery instance
# 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
# like to deliver. The <tt>signup_notification</tt> method defined above is
# delivered by invoking <tt>Notifier.deliver_signup_notification</tt>.
#
# = HTML Email
# To send mail as HTML, make sure your view (the <tt>.rhtml</tt> file) generates HTML and
# set the content type to html.
#
# 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 } # body "account" => recipient
# from "system@example.com" # from "system@example.com"
# content_type "text/html" # Here's where the magic happens
# end # end
# end
# #
# # explicitly specify multipart messages # = Multipart Email
# You can explicitly specify multipart messages:
#
# class ApplicationMailer < 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"
@ -32,7 +104,28 @@ module ActionMailer #:nodoc:
# p.transfer_encoding = "base64" # p.transfer_encoding = "base64"
# end # end
# end # end
# end
# #
# Multipart messages can also be used implicitly because ActionMailer will automatically
# detect and use multipart templates, where each template is named after the name of the action, followed
# by the content type. Each such detected template will be added as separate part to the message.
#
# For example, if the following templates existed:
# * signup_notification.text.plain.rhtml
# * signup_notification.text.html.rhtml
# * signup_notification.text.xml.rxml
# * signup_notification.text.x-yaml.rhtml
#
# 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.
#
# = Attachments
# Attachments can be added by using the +attachment+ method.
#
# Example:
#
# class ApplicationMailer < ActionMailer::Base
# # attachments # # attachments
# def signup_notification(recipient) # def signup_notification(recipient)
# recipients recipient.email_address_with_name # recipients recipient.email_address_with_name
@ -46,37 +139,8 @@ module ActionMailer #:nodoc:
# a.body = generate_your_pdf_here() # a.body = generate_your_pdf_here()
# end # end
# end # end
#
# # implicitly multipart messages
# def signup_notification(recipient)
# recipients recipient.email_address_with_name
# subject "New account information"
# from "system@example.com"
# body(:account => "recipient")
#
# # ActionMailer will automatically detect and use multipart templates,
# # where each template is named after the name of the action, followed
# # by the content type. Each such detected template will be added as
# # a separate part to the message.
# #
# # for example, if the following templates existed:
# # * signup_notification.text.plain.rhtml
# # * signup_notification.text.html.rhtml
# # * signup_notification.text.xml.rxml
# # * signup_notification.text.x-yaml.rhtml
# #
# # 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.
# end
# end # end
# #
# # After this, post_notification will look for "templates/application_mailer/post_notification.rhtml"
# ApplicationMailer.template_root = "templates"
#
# ApplicationMailer.create_comment_notification(david, hello_world) # => a tmail object
# ApplicationMailer.deliver_comment_notification(david, hello_world) # sends the email
#
# = 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>
@ -127,7 +191,7 @@ module ActionMailer #:nodoc:
private_class_method :new #:nodoc: private_class_method :new #:nodoc:
cattr_accessor :template_root class_inheritable_accessor :template_root
cattr_accessor :logger cattr_accessor :logger
@@server_settings = { @@server_settings = {

View file

@ -2,7 +2,7 @@ module ActionMailer
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 1 MAJOR = 1
MINOR = 2 MINOR = 2
TINY = 1 TINY = 4
STRING = [MAJOR, MINOR, TINY].join('.') STRING = [MAJOR, MINOR, TINY].join('.')
end end

View file

@ -54,14 +54,14 @@ 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', '= 1.12.1' + PKG_BUILD) s.add_dependency('actionpack', '= 1.12.4' + PKG_BUILD)
s.has_rdoc = true s.has_rdoc = true
s.requirements << 'none' s.requirements << 'none'
s.require_path = 'lib' s.require_path = 'lib'
s.autorequire = 'action_mailer' s.autorequire = 'action_mailer'
s.files = [ "rakefile", "install.rb", "README", "CHANGELOG", "MIT-LICENSE" ] s.files = [ "Rakefile", "install.rb", "README", "CHANGELOG", "MIT-LICENSE" ]
s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) } s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) } s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end end
@ -84,116 +84,12 @@ task :pdoc => [:rdoc] do
end end
desc "Publish the release files to RubyForge." desc "Publish the release files to RubyForge."
task :release => [:package] do task :release => [ :package ] do
files = ["gem", "tgz", "zip"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" } `rubyforge login`
if RUBY_FORGE_PROJECT then for ext in %w( gem tgz zip )
require 'net/http' release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
require 'open-uri' puts release_command
system(release_command)
project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
project_data = open(project_uri) { |data| data.read }
group_id = project_data[/[?&]group_id=(\d+)/, 1]
raise "Couldn't get group id" unless group_id
# This echos password to shell which is a bit sucky
if ENV["RUBY_FORGE_PASSWORD"]
password = ENV["RUBY_FORGE_PASSWORD"]
else
print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
password = STDIN.gets.chomp
end
login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
data = [
"login=1",
"form_loginname=#{RUBY_FORGE_USER}",
"form_pw=#{password}"
].join("&")
http.post("/account/login.php", data)
end
cookie = login_response["set-cookie"]
raise "Login failed" unless cookie
headers = { "Cookie" => cookie }
release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
release_data = open(release_uri, headers) { |data| data.read }
package_id = release_data[/[?&]package_id=(\d+)/, 1]
raise "Couldn't get package id" unless package_id
first_file = true
release_id = ""
files.each do |filename|
basename = File.basename(filename)
file_ext = File.extname(filename)
file_data = File.open(filename, "rb") { |file| file.read }
puts "Releasing #{basename}..."
release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
type_map = {
".zip" => "3000",
".tgz" => "3110",
".gz" => "3110",
".gem" => "1400"
}; type_map.default = "9999"
type = type_map[file_ext]
boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
query_hash = if first_file then
{
"group_id" => group_id,
"package_id" => package_id,
"release_name" => RELEASE_NAME,
"release_date" => release_date,
"type_id" => type,
"processor_id" => "8000", # Any
"release_notes" => "",
"release_changes" => "",
"preformatted" => "1",
"submit" => "1"
}
else
{
"group_id" => group_id,
"release_id" => release_id,
"package_id" => package_id,
"step2" => "1",
"type_id" => type,
"processor_id" => "8000", # Any
"submit" => "Add This File"
}
end
query = "?" + query_hash.map do |(name, value)|
[name, URI.encode(value)].join("=")
end.join("&")
data = [
"--" + boundary,
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
"Content-Type: application/octet-stream",
"Content-Transfer-Encoding: binary",
"", file_data, ""
].join("\x0D\x0A")
release_headers = headers.merge(
"Content-Type" => "multipart/form-data; boundary=#{boundary}"
)
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
http.post(target + query, data, release_headers)
end
if first_file then
release_id = release_response.body[/release_id=(\d+)/, 1]
raise("Couldn't get release id") unless release_id
end
first_file = false
end
end end
end end

View file

@ -24,6 +24,8 @@ class Net::SMTP
end end
class FunkyPathMailer < ActionMailer::Base class FunkyPathMailer < ActionMailer::Base
self.template_root = "#{File.dirname(__FILE__)}/fixtures/path.with.dots"
def multipart_with_template_path_with_dots(recipient) def multipart_with_template_path_with_dots(recipient)
recipients recipient recipients recipient
subject "Have a lovely picture" subject "Have a lovely picture"
@ -816,3 +818,15 @@ EOF
end end
end end
class InheritableTemplateRootTest < Test::Unit::TestCase
def test_attr
expected = "#{File.dirname(__FILE__)}/fixtures/path.with.dots"
assert_equal expected, FunkyPathMailer.template_root
sub = Class.new(FunkyPathMailer)
sub.template_root = 'test/path'
assert_equal 'test/path', sub.template_root
assert_equal expected, FunkyPathMailer.template_root
end
end

View file

@ -1,4 +1,55 @@
*1.12.1* (April 6th, 2005) *1.12.4* (August 8th, 2006)
* Documentation fix: integration test scripts don't require integration_test. #4914 [Frederick Ros <sl33p3r@free.fr>]
* ActionController::Base Summary documentation rewrite. #4900 [kevin.clark@gmail.com]
* Fix text_helper.rb documentation rendering. #4725 [Frederick Ros]
* Fixes bad rendering of JavaScriptMacrosHelper rdoc. #4910 [Frederick Ros]
* Enhance documentation for setting headers in integration tests. Skip auto HTTP prepending when its already there. #4079 [Rick Olson]
* Documentation for AbstractRequest. #4895 [kevin.clark@gmail.com]
* Remove all remaining references to @params in the documentation. [Marcel Molina Jr.]
* Add documentation for redirect_to :back's RedirectBackError exception. [Marcel Molina Jr.]
* Update layout and content_for documentation to use yield rather than magic @content_for instance variables. [Marcel Molina Jr.]
* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick]
* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [DHH]
* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled. #1897 [jeremye@bsa.ca.gov]
* Fixed that real files and symlinks should be treated the same when compiling templates. #5438 [zachary@panandscan.com]
* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 [Manfred Stienstra <m.stienstra@fngtps.com>]
* Update documentation for erb trim syntax. #5651 [matt@mattmargolis.net]
* Short documentation to mention use of Mime::Type.register. #5710 [choonkeat@gmail.com]
*1.12.3* (June 28th, 2006)
* Fix broken traverse_to_controller. We now:
Look for a _controller.rb file under RAILS_ROOT to load.
If we find it, we require_dependency it and return the controller it defined. (If none was defined we stop looking.)
If we don't find it, we look for a .rb file under RAILS_ROOT to load. If we find it, and it loads a constant we keep looking.
Otherwise we check to see if a directory of the same name exists, and if it does we create a module for it.
*1.12.2* (June 27th, 2006)
* Refinement to avoid exceptions in traverse_to_controller.
* (Hackish) Fix loading of arbitrary files in Ruby's load path by traverse_to_controller. [Nicholas Seckar]
*1.12.1* (April 6th, 2006)
* Fixed that template extensions would be cached development mode #4624 [Stefan Kaes] * Fixed that template extensions would be cached development mode #4624 [Stefan Kaes]
@ -30,7 +81,7 @@
This can be used by deployment managers to set the asset id by application revision This can be used by deployment managers to set the asset id by application revision
*1.12.0* (March 27th, 2005) *1.12.0* (March 27th, 2006)
* Add documentation for respond_to. [Jamis Buck] * Add documentation for respond_to. [Jamis Buck]

View file

@ -469,3 +469,7 @@ And as Jim from Rake says:
For other information, feel free to ask on the ruby-talk mailing list (which For other information, feel free to ask on the ruby-talk mailing list (which
is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com. is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com.
...

53
tracks/vendor/rails/actionpack/filler vendored Normal file
View file

@ -0,0 +1,53 @@
this is just a filler file to try and work around the zlib error when unpacking the gem. Please ignore it, thanks.
abcDEfgHijkLMNopqrStUVWxyz
AbcdefgHIjklMnOpqrsTUvwxYZ
AbCDEfghIjKLmnoPQrstuVwxyz
abcDefGhijKlmnopQrstuvWXYz
AbCdefGhiJKlmnoPqrstuVwxYz
aBcdefGHijKlmnOpQrStUvwxYz
AbcdEfGhIJklmnOPQrSTuvwxyZ
AbcDeFGHijkLmnopqrstuVwxYZ
ABcdefgHIjkLmnOpqrStuVwxyZ
aBcdEFGhiJklmnopQrstuVwxyz
abcDefgHijKlmnoPQrSTuvwxYz
AbcdefGhiJklmnOpqrstuVwxYZ
abcdefgHIjKlMNoPqRsTuvwxYz
ABcDeFghIjklMnopQrstUVwxyZ
AbcdefGhijkLmNopQRstuVWxYZ
aBcdefGhijklMNOpqRsTUvwxyz
abcdEFGhiJKlmnOPQrStUVwxyz
abcDefghIJklmnOPqRStuVWxyz
abcdefGhIjklmnoPQrStUVwXyZ
abcDefghIjkLmnopQrstuVwxyz
AbcdefGhIjklMNOPqrstuvWXyz
AbCdEfGHijkLmnopqrstuvwxyz
abCdEFghijKlmnopqRstuvwXYz
abCdEfghIJklmnOPqrsTUvwxyz
AbcdeFghijklmnoPqrStUvWxyZ
aBcDEFghIJKlmnopqrstuvWXyz
abcdEfghiJKlmNopqrstuvwXyz
AbcdEFGHIJKlmnopqRsTuvwxYz
abcdeFgHiJklmnoPQRsTuvwXYz
abcdEfGhijkLmnOPqrstUvwXYZ
abCDeFGhijklmNopQrstUvwxYz
abCdeFGhIjklmnOpQrstUvwxyZ
aBcDEFgHijKlmNOPQrsTUvwxYz
aBcDefghijklmNoPqrstUvWXyz
AbcDefgHiJklmnOPqRStuvwxYz
aBcdefGHijklMnopqRstUvwxyz
AbCdefghijKLmnopqRstuvWXyz
aBCdefGhiJkLMnopQrsTUvwxyz
ABcdefGHijKlmnOPqrSTUvWXyz
aBCdEfGHIJklMnopqRsTUvWxyz
ABcDEFGHIJklMnopqrSTuVwxyz
abcdEfghijklMnopqrstuvwxyz
AbCDEFghIjkLmNOpQRstUVwxyZ
abCdEFghIJklMNOPqrstUvwXYZ
abCdefghijklmnoPQrstuVwxyz
AbcdEfghijkLMnopqRSTUvWxYz
ABcDEfGhIjKLmNopqrStuVwxyZ
abCdefgHijklmnOpQRStuvwxYz
abCdeFghijKLmNopQrstuvwxyZ
abcdEFGHijKlmnopqrstuvwxYZ

View file

@ -49,13 +49,15 @@ module ActionController #:nodoc:
end end
end end
# Action Controllers are made up of one or more actions that performs its purpose and then either renders a template or # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
# redirects to another action. An action is defined as a public method on the controller, which will automatically be # on request and then either render a template or redirect to another action. An action is defined as a public method
# made accessible to the web-server through a mod_rewrite mapping. A sample controller could look like this: # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
#
# A sample controller could look like this:
# #
# class GuestBookController < ActionController::Base # class GuestBookController < ActionController::Base
# def index # def index
# @entries = Entry.find_all # @entries = Entry.find(:all)
# end # end
# #
# def sign # def sign
@ -64,26 +66,17 @@ module ActionController #:nodoc:
# end # end
# end # end
# #
# GuestBookController.template_root = "templates/" # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
# GuestBookController.process_cgi # after executing code in the action. For example, the +index+ action of the +GuestBookController+ would render the
# template <tt>app/views/guestbook/index.rhtml</tt> by default after populating the <tt>@entries</tt> instance variable.
# #
# All actions assume that you want to render a template matching the name of the action at the end of the performance # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
# unless you tell it otherwise. The index action complies with this assumption, so after populating the @entries instance # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
# variable, the GuestBookController will render "templates/guestbook/index.rhtml". # "302 Moved" HTTP response that takes the user to the index action.
#
# Unlike index, the sign action isn't interested in rendering a template. So after performing its main purpose (creating a
# new entry in the guest book), it sheds the rendering assumption and initiates a redirect instead. This redirect works by
# returning an external "302 Moved" HTTP response that takes the user to the index action.
# #
# The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect. # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
# Most actions are variations of these themes. # Most actions are variations of these themes.
# #
# Also note that it's the final call to <tt>process_cgi</tt> that actually initiates the action performance. It will extract
# request and response objects from the CGI
#
# When Action Pack is used inside of Rails, the template_root is automatically configured and you don't need to call process_cgi
# yourself.
#
# == Requests # == Requests
# #
# Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters. # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters.
@ -94,16 +87,16 @@ module ActionController #:nodoc:
# The full request object is available with the request accessor and is primarily used to query for http headers. These queries # The full request object is available with the request accessor and is primarily used to query for http headers. These queries
# are made by accessing the environment hash, like this: # are made by accessing the environment hash, like this:
# #
# def hello_ip # def server_ip
# location = request.env["REMOTE_IP"] # location = request.env["SERVER_ADDR"]
# render :text => "Hello stranger from #{location}" # render :text => "This server hosted at #{location}"
# end # end
# #
# == Parameters # == Parameters
# #
# All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params hash. # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
# So an action that was performed through /weblog/list?category=All&limit=5 will include { "category" => "All", "limit" => 5 } # which returns a hash. For example, an action that was performed through <tt>/weblog/list?category=All&limit=5</tt> will include
# in params. # <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
# #
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as: # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
# #
@ -116,12 +109,12 @@ module ActionController #:nodoc:
# #
# == Sessions # == Sessions
# #
# Sessions allows you to store objects in memory between requests. This is useful for objects that are not yet ready to be persisted, # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
# such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
# as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
# they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at. # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
# #
# You can place objects in the session by using the <tt>session</tt> hash accessor: # You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
# #
# session[:person] = Person.authenticate(user_name, password) # session[:person] = Person.authenticate(user_name, password)
# #
@ -129,17 +122,24 @@ module ActionController #:nodoc:
# #
# Hello #{session[:person]} # Hello #{session[:person]}
# #
# Any object can be placed in the session (as long as it can be Marshalled). But remember that 1000 active sessions each storing a
# 50kb object could lead to a 50MB memory overhead. In other words, think carefully about size and caching before resorting to the use
# of the session.
#
# For removing objects from the session, you can either assign a single key to nil, like <tt>session[:person] = nil</tt>, or you can # For removing objects from the session, you can either assign a single key to nil, like <tt>session[:person] = nil</tt>, or you can
# remove the entire session with reset_session. # remove the entire session with reset_session.
# #
# By default, sessions are stored on the file system in <tt>RAILS_ROOT/tmp/sessions</tt>. Any object can be placed in the session
# (as long as it can be Marshalled). But remember that 1000 active sessions each storing a 50kb object could lead to a 50MB store on the filesystem.
# In other words, think carefully about size and caching before resorting to the use of the session on the filesystem.
#
# An alternative to storing sessions on disk is to use ActiveRecordStore to store sessions in your database, which can solve problems
# caused by storing sessions in the file system and may speed up your application. To use ActiveRecordStore, uncomment the line:
#
# config.action_controller.session_store = :active_record_store
#
# in your <tt>environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
#
# == Responses # == Responses
# #
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
# object is generated automatically through the use of renders and redirects, so it's normally nothing you'll need to be concerned about. # object is generated automatically through the use of renders and redirects and requires no user intervention.
# #
# == Renders # == Renders
# #
@ -161,9 +161,9 @@ module ActionController #:nodoc:
# def search # def search
# @results = Search.find(params[:query]) # @results = Search.find(params[:query])
# case @results # case @results
# when 0 then render :action=> "no_results" # when 0 then render :action => "no_results"
# when 1 then render :action=> "show" # when 1 then render :action => "show"
# when 2..10 then render :action=> "show_many" # when 2..10 then render :action => "show_many"
# end # end
# end # end
# #
@ -171,32 +171,21 @@ module ActionController #:nodoc:
# #
# == Redirects # == Redirects
# #
# Redirecting is what actions that update the model do when they're done. The <tt>save_post</tt> method shouldn't be responsible for also # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to a database,
# showing the post once it's saved -- that's the job for <tt>show_post</tt>. So once <tt>save_post</tt> has completed its business, it'll # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
# redirect to <tt>show_post</tt>. All redirects are external, which means that when the user refreshes his browser, it's not going to save # a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
# the post again, but rather just show it one more time.
# #
# This sounds fairly simple, but the redirection is complicated by the quest for a phenomenon known as "pretty urls". Instead of accepting # def create
# the dreadful being that is "weblog_controller?action=show&post_id=5", Action Controller goes out of its way to represent the former as # @entry = Entry.new(params[:entry])
# "/weblog/show/5". And this is even the simple case. As an example of a more advanced pretty url consider # if @entry.save
# "/library/books/ISBN/0743536703/show", which can be mapped to books_controller?action=show&type=ISBN&id=0743536703. # # The entry was saved correctly, redirect to show
# redirect_to :action => 'show', :id => @entry.id
# else
# # things didn't go so well, do something else
# end
# end
# #
# Redirects work by rewriting the URL of the current action. So if the show action was called by "/library/books/ISBN/0743536703/show", # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
# we can redirect to an edit action simply by doing <tt>redirect_to(:action => "edit")</tt>, which could throw the user to
# "/library/books/ISBN/0743536703/edit". Naturally, you'll need to setup the routes configuration file to point to the proper controller
# and action in the first place, but once you have, it can be rewritten with ease.
#
# Let's consider a bunch of examples on how to go from "/clients/37signals/basecamp/project/dash" to somewhere else:
#
# redirect_to(:action => "edit") =>
# /clients/37signals/basecamp/project/dash
#
# redirect_to(:client_name => "nextangle", :project_name => "rails") =>
# /clients/nextangle/rails/project/dash
#
# Those redirects happen under the configuration of:
#
# map.connect 'clients/:client_name/:project_name/:controller/:action'
# #
# == Calling multiple redirects or renders # == Calling multiple redirects or renders
# #
@ -214,15 +203,6 @@ module ActionController #:nodoc:
# render :action => "overthere" # won't be called unless monkeys is nil # render :action => "overthere" # won't be called unless monkeys is nil
# end # end
# #
# == Environments
#
# Action Controller works out of the box with CGI, FastCGI, and mod_ruby. CGI and mod_ruby controllers are triggered just the same using:
#
# WeblogController.process_cgi
#
# FastCGI controllers are triggered using:
#
# FCGI.each_cgi{ |cgi| WeblogController.process_cgi(cgi) }
class Base class Base
DEFAULT_RENDER_STATUS_CODE = "200 OK" DEFAULT_RENDER_STATUS_CODE = "200 OK"
@ -263,10 +243,10 @@ module ActionController #:nodoc:
# Modern REST web services often need to submit complex data to the web application. # Modern REST web services often need to submit complex data to the web application.
# The param_parsers hash lets you register handlers wich will process the http body and add parameters to the # The param_parsers hash lets you register handlers wich will process the http body and add parameters to the
# @params hash. These handlers are invoked for post and put requests. # <tt>params</tt> hash. These handlers are invoked for post and put requests.
# #
# By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instanciated # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instanciated
# in the @params. This allows XML requests to mask themselves as regular form submissions, so you can have one # in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one
# action serve both regular forms and web service requests. # action serve both regular forms and web service requests.
# #
# Example of doing your own parser for a custom content type: # Example of doing your own parser for a custom content type:
@ -366,6 +346,53 @@ module ActionController #:nodoc:
def hide_action(*names) def hide_action(*names)
write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect { |n| n.to_s }) write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect { |n| n.to_s })
end end
# Replace sensitive paramater data from the request log.
# Filters paramaters that have any of the arguments as a substring.
# Looks in all subhashes of the param hash for keys to filter.
# If a block is given, each key and value of the paramater hash and all
# subhashes is passed to it, the value or key
# can be replaced using String#replace or similar method.
#
# Examples:
# filter_parameter_logging
# => Does nothing, just slows the logging process down
#
# filter_parameter_logging :password
# => replaces the value to all keys matching /password/i with "[FILTERED]"
#
# filter_parameter_logging :foo, "bar"
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
#
# filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i
#
# filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i, and
# replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
def filter_parameter_logging(*filter_words, &block)
parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
define_method(:filter_parameters) do |unfiltered_parameters|
filtered_parameters = {}
unfiltered_parameters.each do |key, value|
if key =~ parameter_filter
filtered_parameters[key] = '[FILTERED]'
elsif value.is_a?(Hash)
filtered_parameters[key] = filter_parameters(value)
elsif block_given?
key, value = key.dup, value.dup
yield key, value
filtered_parameters[key] = value
else
filtered_parameters[key] = value
end
end
filtered_parameters
end
end
end end
public public
@ -803,6 +830,10 @@ module ActionController #:nodoc:
# redirect_to :back # redirect_to :back
# #
# The redirection happens as a "302 Moved" header. # The redirection happens as a "302 Moved" header.
#
# When using <tt>redirect_to :back</tt>, if there is no referrer,
# RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescueing RedirectBackError.
def redirect_to(options = {}, *parameters_for_method_reference) #:doc: def redirect_to(options = {}, *parameters_for_method_reference) #:doc:
case options case options
when %r{^\w+://.*} when %r{^\w+://.*}
@ -901,7 +932,7 @@ module ActionController #:nodoc:
if logger if logger
logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]" logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
logger.info " Session ID: #{@session.session_id}" if @session and @session.respond_to?(:session_id) logger.info " Session ID: #{@session.session_id}" if @session and @session.respond_to?(:session_id)
logger.info " Parameters: #{@params.inspect}" logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(@params).inspect : @params.inspect}"
end end
end end

View file

@ -38,9 +38,9 @@ module ActionController #:nodoc:
# #
# class WeblogController < ActionController::Base # class WeblogController < ActionController::Base
# def update # def update
# List.update(@params["list"]["id"], @params["list"]) # List.update(params[:list][:id], params[:list])
# expire_page :action => "show", :id => @params["list"]["id"] # expire_page :action => "show", :id => params[:list][:id]
# redirect_to :action => "show", :id => @params["list"]["id"] # redirect_to :action => "show", :id => params[:list][:id]
# end # end
# end # end
# #

View file

@ -64,11 +64,12 @@ module ActionController #:nodoc:
end end
def request_parameters def request_parameters
if ActionController::Base.param_parsers.has_key?(content_type) @request_parameters ||=
CGIMethods.parse_formatted_request_parameters(content_type, @env['RAW_POST_DATA']) if ActionController::Base.param_parsers.has_key?(content_type)
else CGIMethods.parse_formatted_request_parameters(content_type, @env['RAW_POST_DATA'])
CGIMethods.parse_request_parameters(@cgi.params) else
end CGIMethods.parse_request_parameters(@cgi.params)
end
end end
def cookies def cookies

View file

@ -140,14 +140,18 @@ module ActionController
# Performs a GET request with the given parameters. The parameters may # Performs a GET request with the given parameters. The parameters may
# be +nil+, a Hash, or a string that is appropriately encoded # be +nil+, a Hash, or a string that is appropriately encoded
# (application/x-www-form-urlencoded or multipart/form-data). # (application/x-www-form-urlencoded or multipart/form-data). The headers
# should be a hash. The keys will automatically be upcased, with the
# prefix 'HTTP_' added if needed.
def get(path, parameters=nil, headers=nil) def get(path, parameters=nil, headers=nil)
process :get, path, parameters, headers process :get, path, parameters, headers
end end
# Performs a POST request with the given parameters. The parameters may # Performs a POST request with the given parameters. The parameters may
# be +nil+, a Hash, or a string that is appropriately encoded # be +nil+, a Hash, or a string that is appropriately encoded
# (application/x-www-form-urlencoded or multipart/form-data). # (application/x-www-form-urlencoded or multipart/form-data). The headers
# should be a hash. The keys will automatically be upcased, with the
# prefix 'HTTP_' added if needed.
def post(path, parameters=nil, headers=nil) def post(path, parameters=nil, headers=nil)
process :post, path, parameters, headers process :post, path, parameters, headers
end end
@ -155,7 +159,9 @@ module ActionController
# Performs an XMLHttpRequest request with the given parameters, mimicing # Performs an XMLHttpRequest request with the given parameters, mimicing
# the request environment created by the Prototype library. The parameters # the request environment created by the Prototype library. The parameters
# may be +nil+, a Hash, or a string that is appropriately encoded # may be +nil+, a Hash, or a string that is appropriately encoded
# (application/x-www-form-urlencoded or multipart/form-data). # (application/x-www-form-urlencoded or multipart/form-data). The headers
# should be a hash. The keys will automatically be upcased, with the
# prefix 'HTTP_' added if needed.
def xml_http_request(path, parameters=nil, headers=nil) def xml_http_request(path, parameters=nil, headers=nil)
headers = (headers || {}).merge("X-Requested-With" => "XMLHttpRequest") headers = (headers || {}).merge("X-Requested-With" => "XMLHttpRequest")
post(path, parameters, headers) post(path, parameters, headers)
@ -218,7 +224,7 @@ module ActionController
(headers || {}).each do |key, value| (headers || {}).each do |key, value|
key = key.to_s.upcase.gsub(/-/, "_") key = key.to_s.upcase.gsub(/-/, "_")
key = "HTTP_#{key}" unless env.has_key?(key) key = "HTTP_#{key}" unless env.has_key?(key) || env =~ /^X|HTTP/
env[key] = value env[key] = value
end end
@ -341,7 +347,6 @@ module ActionController
# using the get/post methods: # using the get/post methods:
# #
# require "#{File.dirname(__FILE__)}/test_helper" # require "#{File.dirname(__FILE__)}/test_helper"
# require "integration_test"
# #
# class ExampleTest < ActionController::IntegrationTest # class ExampleTest < ActionController::IntegrationTest
# fixtures :people # fixtures :people
@ -366,7 +371,6 @@ module ActionController
# reference any named routes you happen to have defined! # reference any named routes you happen to have defined!
# #
# require "#{File.dirname(__FILE__)}/test_helper" # require "#{File.dirname(__FILE__)}/test_helper"
# require "integration_test"
# #
# class AdvancedTest < ActionController::IntegrationTest # class AdvancedTest < ActionController::IntegrationTest
# fixtures :people, :rooms # fixtures :people, :rooms

View file

@ -27,7 +27,7 @@ module ActionController #:nodoc:
# that the header and footer are only mentioned in one place, like this: # that the header and footer are only mentioned in one place, like this:
# #
# <!-- The header part of this layout --> # <!-- The header part of this layout -->
# <%= @content_for_layout %> # <%= yield %>
# <!-- The footer part of this layout --> # <!-- The footer part of this layout -->
# #
# And then you have content pages that look like this: # And then you have content pages that look like this:
@ -47,7 +47,7 @@ module ActionController #:nodoc:
# references that won't materialize before rendering time: # references that won't materialize before rendering time:
# #
# <h1><%= @page_title %></h1> # <h1><%= @page_title %></h1>
# <%= @content_for_layout %> # <%= yield %>
# #
# ...and content pages that fulfill these references _at_ rendering time: # ...and content pages that fulfill these references _at_ rendering time:
# #
@ -159,10 +159,12 @@ module ActionController #:nodoc:
# #
# As you can see, you pass the template as the first parameter, the status code as the second ("200" is OK), and the layout # As you can see, you pass the template as the first parameter, the status code as the second ("200" is OK), and the layout
# as the third. # as the third.
#
# NOTE: The old notation for rendering the view from a layout was to expose the magic <tt>@content_for_layout</tt> instance
# variable. The preferred notation now is to use <tt>yield</tt>, as documented above.
module ClassMethods module ClassMethods
# If a layout is specified, all actions rendered through render and render_action will have their result assigned # If a layout is specified, all rendered actions will have their result rendered
# to <tt>@content_for_layout</tt>, which can then be used by the layout to insert their contents with # when the layout<tt>yield</tt>'s. This layout can itself depend on instance variables assigned during action
# <tt><%= @content_for_layout %></tt>. This layout can itself depend on instance variables assigned during action
# performance and have access to them as any normal template would. # performance and have access to them as any normal template would.
def layout(template_name, conditions = {}) def layout(template_name, conditions = {})
add_layout_conditions(conditions) add_layout_conditions(conditions)

View file

@ -92,6 +92,12 @@ module ActionController #:nodoc:
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
# in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow # in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow
# and accept Rails' defaults, life will be much easier. # and accept Rails' defaults, life will be much easier.
#
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
# environment.rb as follows.
#
# Mime::Type.register "image/jpg", :jpg
#
def respond_to(*types, &block) def respond_to(*types, &block)
raise ArgumentError, "respond_to takes either types or a block, never bot" unless types.any? ^ block raise ArgumentError, "respond_to takes either types or a block, never bot" unless types.any? ^ block
block ||= lambda { |responder| types.each { |type| responder.send(type) } } block ||= lambda { |responder| types.each { |type| responder.send(type) } }

View file

@ -31,7 +31,7 @@ module ActionController
# instance variable, which is an ordered collection of model objects for the # instance variable, which is an ordered collection of model objects for the
# current page (at most 20, sorted by last name and first name), and a # current page (at most 20, sorted by last name and first name), and a
# <tt>@person_pages</tt> Paginator instance. The current page is determined # <tt>@person_pages</tt> Paginator instance. The current page is determined
# by the <tt>@params['page']</tt> variable. # by the <tt>params[:page]</tt> variable.
# #
# ==== Pagination for a single action # ==== Pagination for a single action
# #
@ -47,7 +47,7 @@ module ActionController
# ==== Custom/"classic" pagination # ==== Custom/"classic" pagination
# #
# def list # def list
# @person_pages = Paginator.new self, Person.count, 10, @params['page'] # @person_pages = Paginator.new self, Person.count, 10, params[:page]
# @people = Person.find :all, :order => 'last_name, first_name', # @people = Person.find :all, :order => 'last_name, first_name',
# :limit => @person_pages.items_per_page, # :limit => @person_pages.items_per_page,
# :offset => @person_pages.current.offset # :offset => @person_pages.current.offset

View file

@ -1,5 +1,6 @@
module ActionController module ActionController
# These methods are available in both the production and test Request objects. # Subclassing AbstractRequest makes these methods available to the request objects used in production and testing,
# CgiRequest and TestRequest
class AbstractRequest class AbstractRequest
cattr_accessor :relative_url_root cattr_accessor :relative_url_root
@ -65,6 +66,7 @@ module ActionController
end end
end end
# Returns the accepted MIME type for the request
def accepts def accepts
@accepts ||= @accepts ||=
if @env['HTTP_ACCEPT'].to_s.strip.empty? if @env['HTTP_ACCEPT'].to_s.strip.empty?
@ -202,15 +204,21 @@ module ActionController
host + port_string host + port_string
end end
def path_parameters=(parameters) def path_parameters=(parameters) #:nodoc:
@path_parameters = parameters @path_parameters = parameters
@symbolized_path_parameters = @parameters = nil @symbolized_path_parameters = @parameters = nil
end end
# The same as <tt>path_parameters</tt> with explicitly symbolized keys
def symbolized_path_parameters def symbolized_path_parameters
@symbolized_path_parameters ||= path_parameters.symbolize_keys @symbolized_path_parameters ||= path_parameters.symbolize_keys
end end
# Returns a hash with the parameters used to form the path of the request
#
# Example:
#
# {:action => 'my_action', :controller => 'my_controller'}
def path_parameters def path_parameters
@path_parameters ||= {} @path_parameters ||= {}
end end

View file

@ -219,42 +219,80 @@ module ActionController
g.result :controller, expr, true g.result :controller, expr, true
end end
def file_kinds(kind)
((@file_kinds ||= []) << kind).uniq! || @file_kinds
end
def traverse_to_controller(segments, start_at = 0) def traverse_to_controller(segments, start_at = 0)
mod = ::Object mod = ::Object
length = segments.length length = segments.length
index = start_at index = start_at
mod_name = controller_name = segment = nil mod_name = controller_name = segment = nil
while index < length while index < length
return nil unless /^[A-Za-z][A-Za-z\d_]*$/ =~ (segment = segments[index]) return nil unless /\A[A-Za-z][A-Za-z\d_]*\Z/ =~ (segment = segments[index])
index += 1 index += 1
file_kinds :app
mod_name = segment.camelize mod_name = segment.camelize
controller_name = "#{mod_name}Controller" controller_name = "#{mod_name}Controller"
path_suffix = File.join(segments[start_at..(index - 1)])
next_mod = nil
begin # If the controller is already present, or if we load it, return it.
# We use eval instead of const_get to avoid obtaining values from parent modules. if mod.const_defined?(controller_name) || attempt_load(mod, controller_name, path_suffix + "_controller") == :defined
controller = eval("mod::#{controller_name}", nil, __FILE__, __LINE__) controller = mod.const_get(controller_name)
expected_name = "#{mod.name}::#{controller_name}" return nil unless controller.is_a?(Class) && controller.ancestors.include?(ActionController::Base) # it's not really a controller?
return [controller, (index - start_at)]
end
# Detect the case when const_get returns an object from a parent namespace. # No controller? Look for the module
if controller.is_a?(Class) && controller.ancestors.include?(ActionController::Base) && (mod == Object || controller.name == expected_name) if mod.const_defined? mod_name
return controller, (index - start_at) next_mod = mod.send(:const_get, mod_name)
next_mod = nil unless next_mod.is_a?(Module)
else
# Try to load a file that defines the module we want.
case attempt_load(mod, mod_name, path_suffix)
when :defined then next_mod = mod.const_get mod_name
when :dir then # We didn't find a file, but there's a dir.
next_mod = Module.new # So create a module for the directory
mod.send :const_set, mod_name, next_mod
else
return nil
end end
rescue NameError => e
raise unless /^uninitialized constant .*#{controller_name}$/ =~ e.message
end end
mod = next_mod
begin return nil unless mod && mod.is_a?(Module)
next_mod = eval("mod::#{mod_name}", nil, __FILE__, __LINE__)
# Check that we didn't get a module from a parent namespace
mod = (mod == Object || next_mod.name == "#{mod.name}::#{mod_name}") ? next_mod : nil
rescue NameError => e
raise unless /^uninitialized constant .*#{mod_name}$/ =~ e.message
end
return nil unless mod
end end
nil
end
protected
def safe_load_paths #:nodoc:
if defined?(RAILS_ROOT)
$LOAD_PATH.select do |base|
base = File.expand_path(base)
extended_root = File.expand_path(RAILS_ROOT)
base.match(/\A#{Regexp.escape(extended_root)}\/*#{file_kinds(:lib) * '|'}/) || base =~ %r{rails-[\d.]+/builtin}
end
else
$LOAD_PATH
end
end
def attempt_load(mod, const_name, path)
has_dir = false
safe_load_paths.each do |load_path|
full_path = File.join(load_path, path)
file_path = full_path + '.rb'
if File.file?(file_path) # Found a .rb file? Load it up
require_dependency(file_path)
return :defined if mod.const_defined? const_name
else
has_dir ||= File.directory?(full_path)
end
end
return (has_dir ? :dir : nil)
end end
end end
end end

View file

@ -14,7 +14,7 @@ module ActionController #:nodoc:
# it feasible to send even large files. # it feasible to send even large files.
# #
# Be careful to sanitize the path parameter if it coming from a web # Be careful to sanitize the path parameter if it coming from a web
# page. send_file(@params['path']) allows a malicious user to # page. send_file(params[:path]) allows a malicious user to
# download any file on your server. # download any file on your server.
# #
# Options: # Options:
@ -28,6 +28,7 @@ module ActionController #:nodoc:
# or to read the entire file before sending (false). Defaults to true. # or to read the entire file before sending (false). Defaults to true.
# * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file. # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
# Defaults to 4096. # Defaults to 4096.
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
# #
# The default Content-Type and Content-Disposition headers are # The default Content-Type and Content-Disposition headers are
# set to download arbitrary binary files in as many browsers as # set to download arbitrary binary files in as many browsers as
@ -37,9 +38,12 @@ module ActionController #:nodoc:
# Simple download: # Simple download:
# send_file '/path/to.zip' # send_file '/path/to.zip'
# #
# Show a JPEG in browser: # Show a JPEG in the browser:
# send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline' # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
# #
# Show a 404 page in the browser:
# send_file '/path/to/404.html, :type => 'text/html; charset=utf-8', :status => 404
#
# Read about the other Content-* HTTP headers if you'd like to # Read about the other Content-* HTTP headers if you'd like to
# provide the user with more information (such as Content-Description). # provide the user with more information (such as Content-Description).
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
@ -61,7 +65,7 @@ module ActionController #:nodoc:
@performed_render = false @performed_render = false
if options[:stream] if options[:stream]
render :text => Proc.new { |response, output| render :status => options[:status], :text => Proc.new { |response, output|
logger.info "Streaming file #{path}" unless logger.nil? logger.info "Streaming file #{path}" unless logger.nil?
len = options[:buffer_size] || 4096 len = options[:buffer_size] || 4096
File.open(path, 'rb') do |file| File.open(path, 'rb') do |file|
@ -81,7 +85,7 @@ module ActionController #:nodoc:
} }
else else
logger.info "Sending file #{path}" unless logger.nil? logger.info "Sending file #{path}" unless logger.nil?
File.open(path, 'rb') { |file| render :text => file.read } File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
end end
end end
@ -93,6 +97,7 @@ module ActionController #:nodoc:
# * <tt>:type</tt> - specifies an HTTP content type. # * <tt>:type</tt> - specifies an HTTP content type.
# Defaults to 'application/octet-stream'. # Defaults to 'application/octet-stream'.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded. # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
# Valid values are 'inline' and 'attachment' (default). # Valid values are 'inline' and 'attachment' (default).
# #
# Generic data download: # Generic data download:
@ -109,7 +114,7 @@ module ActionController #:nodoc:
logger.info "Sending data #{options[:filename]}" unless logger.nil? logger.info "Sending data #{options[:filename]}" unless logger.nil?
send_file_headers! options.merge(:length => data.size) send_file_headers! options.merge(:length => data.size)
@performed_render = false @performed_render = false
render :text => data render :status => options[:status], :text => data
end end
private private

View file

@ -63,7 +63,7 @@
<p style="color: green"><%= flash[:notice] %></p> <p style="color: green"><%= flash[:notice] %></p>
<%= @content_for_layout %> <%= yield %>
</body> </body>
</html> </html>

View file

@ -37,7 +37,7 @@ module ActionController #:nodoc:
# is a hash consisting of the following key/value pairs: # is a hash consisting of the following key/value pairs:
# #
# * <tt>:params</tt>: a single key or an array of keys that must # * <tt>:params</tt>: a single key or an array of keys that must
# be in the @params hash in order for the action(s) to be safely # be in the <tt>params</tt> hash in order for the action(s) to be safely
# called. # called.
# * <tt>:session</tt>: a single key or an array of keys that must # * <tt>:session</tt>: a single key or an array of keys that must
# be in the @session in order for the action(s) to be safely called. # be in the @session in order for the action(s) to be safely called.

View file

@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 1 MAJOR = 1
MINOR = 12 MINOR = 12
TINY = 1 TINY = 4
STRING = [MAJOR, MINOR, TINY].join('.') STRING = [MAJOR, MINOR, TINY].join('.')
end end

View file

@ -11,7 +11,7 @@ module ActionView #:nodoc:
# #
# = ERb # = ERb
# #
# You trigger ERb by using embeddings such as <% %> and <%= %>. The difference is whether you want output or not. Consider the # You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
# following loop for names: # following loop for names:
# #
# <b>Names of all the people</b> # <b>Names of all the people</b>
@ -19,12 +19,14 @@ module ActionView #:nodoc:
# Name: <%= person.name %><br/> # Name: <%= person.name %><br/>
# <% end %> # <% end %>
# #
# The loop is setup in regular embedding tags (<% %>) and the name is written using the output embedding tag (<%= %>). Note that this # The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong: # is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong:
# #
# Hi, Mr. <% puts "Frodo" %> # Hi, Mr. <% puts "Frodo" %>
# #
# (If you absolutely must write from within a function, you can use the TextHelper#concat) # If you absolutely must write from within a function, you can use the TextHelper#concat
#
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
# #
# == Using sub templates # == Using sub templates
# #
@ -425,7 +427,8 @@ module ActionView #:nodoc:
if @@compile_time[render_symbol] && supports_local_assigns?(render_symbol, local_assigns) if @@compile_time[render_symbol] && supports_local_assigns?(render_symbol, local_assigns)
if file_name && !@@cache_template_loading if file_name && !@@cache_template_loading
@@compile_time[render_symbol] < File.mtime(file_name) @@compile_time[render_symbol] < File.mtime(file_name) || (File.symlink?(file_name) ?
@@compile_time[render_symbol] < File.lstat(file_name).mtime : false)
end end
else else
true true

View file

@ -1,6 +1,6 @@
module ActionView module ActionView
module Helpers module Helpers
# Capture lets you extract parts of code into instance variables which # Capture lets you extract parts of code which
# can be used in other points of the template or even layout file. # can be used in other points of the template or even layout file.
# #
# == Capturing a block into an instance variable # == Capturing a block into an instance variable
@ -9,11 +9,10 @@ module ActionView
# [some html...] # [some html...]
# <% end %> # <% end %>
# #
#
# == Add javascript to header using content_for # == Add javascript to header using content_for
# #
# content_for("name") is a wrapper for capture which will store the # content_for("name") is a wrapper for capture which will
# fragment in a instance variable similar to @content_for_layout. # make the fragment available by name to a yielding layout or template.
# #
# layout.rhtml: # layout.rhtml:
# #
@ -21,11 +20,11 @@ module ActionView
# <head> # <head>
# <title>layout with js</title> # <title>layout with js</title>
# <script type="text/javascript"> # <script type="text/javascript">
# <%= @content_for_script %> # <%= yield :script %>
# </script> # </script>
# </head> # </head>
# <body> # <body>
# <%= @content_for_layout %> # <%= yield %>
# </body> # </body>
# </html> # </html>
# #
@ -69,13 +68,9 @@ module ActionView
end end
end end
# Content_for will store the given block # Calling content_for stores the block of markup for later use.
# in an instance variable for later use in another template # Subsequently, you can make calls to it by name with <tt>yield</tt>
# or in the layout. # in another template or in the layout.
#
# The name of the instance variable is content_for_<name>
# to stay consistent with @content_for_layout which is used
# by ActionView's layouts
# #
# Example: # Example:
# #
@ -83,10 +78,17 @@ module ActionView
# alert('hello world') # alert('hello world')
# <% end %> # <% end %>
# #
# You can use @content_for_header anywhere in your templates. # You can use yield :header anywhere in your templates.
#
# <%= yield :header %>
# #
# NOTE: Beware that content_for is ignored in caches. So you shouldn't use it # NOTE: Beware that content_for is ignored in caches. So you shouldn't use it
# for elements that are going to be fragment cached. # for elements that are going to be fragment cached.
#
# The deprecated way of accessing a content_for block was to use a instance variable
# named @@content_for_#{name_of_the_content_block}@. So <tt><%= content_for('footer') %></tt>
# would be avaiable as <tt><%= @content_for_footer %></tt>. The preferred notation now is
# <tt><%= yield :footer %></tt>.
def content_for(name, &block) def content_for(name, &block)
eval "@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)" eval "@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)"
end end

View file

@ -27,6 +27,7 @@ module ActionView
# <tt>:url</tt>:: Specifies the url where the updated value should # <tt>:url</tt>:: Specifies the url where the updated value should
# be sent after the user presses "ok". # be sent after the user presses "ok".
# #
#
# Addtional +options+ are: # Addtional +options+ are:
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA) # <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
# <tt>:cols</tt>:: Number of characters the text input should span (works for both INPUT and TEXTAREA) # <tt>:cols</tt>:: Number of characters the text input should span (works for both INPUT and TEXTAREA)
@ -122,10 +123,10 @@ module ActionView
# <tt>:on_show</tt>:: Like on_hide, only now the expression is called # <tt>:on_show</tt>:: Like on_hide, only now the expression is called
# then the div is shown. # then the div is shown.
# <tt>:after_update_element</tt>:: A Javascript expression that is called when the # <tt>:after_update_element</tt>:: A Javascript expression that is called when the
# user has selected one of the proposed values. # user has selected one of the proposed values.
# The expression should take two variables: element and value. # The expression should take two variables: element and value.
# Element is a DOM element for the field, value # Element is a DOM element for the field, value
# is the value selected by the user. # is the value selected by the user.
# <tt>:select</tt>:: Pick the class of the element from which the value for # <tt>:select</tt>:: Pick the class of the element from which the value for
# insertion should be extracted. If this is not specified, # insertion should be extracted. If this is not specified,
# the entire element is used. # the entire element is used.

View file

@ -143,7 +143,7 @@ module ActionView
# background instead of the regular reloading POST arrangement. Even # background instead of the regular reloading POST arrangement. Even
# though it's using JavaScript to serialize the form elements, the form # though it's using JavaScript to serialize the form elements, the form
# submission will work just like a regular submission as viewed by the # submission will work just like a regular submission as viewed by the
# receiving side (all elements available in @params). The options for # receiving side (all elements available in <tt>params</tt>). The options for
# specifying the target with :url and defining callbacks is the same as # specifying the target with :url and defining callbacks is the same as
# link_to_remote. # link_to_remote.
# #
@ -171,9 +171,10 @@ module ActionView
end end
# Works like form_remote_tag, but uses form_for semantics. # Works like form_remote_tag, but uses form_for semantics.
def remote_form_for(object_name, object, options = {}, &proc) def remote_form_for(object_name, *args, &proc)
options = args.last.is_a?(Hash) ? args.pop : {}
concat(form_remote_tag(options), proc.binding) concat(form_remote_tag(options), proc.binding)
fields_for(object_name, object, options, &proc) fields_for(object_name, *(args << options), &proc)
concat('</form>', proc.binding) concat('</form>', proc.binding)
end end
alias_method :form_remote_for, :remote_form_for alias_method :form_remote_for, :remote_form_for

View file

@ -77,7 +77,7 @@ module ActionView
end end
begin begin
require_library_or_gem "redcloth" require_library_or_gem "redcloth" unless Object.const_defined?(:RedCloth)
# Returns the text with all the Textile codes turned into HTML-tags. # Returns the text with all the Textile codes turned into HTML-tags.
# <i>This method is only available if RedCloth can be required</i>. # <i>This method is only available if RedCloth can be required</i>.
@ -104,7 +104,7 @@ module ActionView
end end
begin begin
require_library_or_gem "bluecloth" require_library_or_gem "bluecloth" unless Object.const_defined?(:BlueCloth)
# Returns the text with all the Markdown codes turned into HTML-tags. # Returns the text with all the Markdown codes turned into HTML-tags.
# <i>This method is only available if BlueCloth can be required</i>. # <i>This method is only available if BlueCloth can be required</i>.
@ -116,7 +116,7 @@ module ActionView
end end
# Returns +text+ transformed into HTML using very simple formatting rules # Returns +text+ transformed into HTML using very simple formatting rules
# Surrounds paragraphs with <tt>&lt;p&gt;</tt> tags, and converts line breaks into <tt>&lt;br /&gt;</tt> # Surrounds paragraphs with <tt><p></tt> tags, and converts line breaks into <tt><br/></tt>
# Two consecutive newlines(<tt>\n\n</tt>) are considered as a paragraph, one newline (<tt>\n</tt>) is # Two consecutive newlines(<tt>\n\n</tt>) are considered as a paragraph, one newline (<tt>\n</tt>) is
# considered a linebreak, three or more consecutive newlines are turned into two newlines # considered a linebreak, three or more consecutive newlines are turned into two newlines
def simple_format(text) def simple_format(text)
@ -129,7 +129,7 @@ module ActionView
end end
# Turns all urls and email addresses into clickable links. The +link+ parameter can limit what should be linked. # Turns all urls and email addresses into clickable links. The +link+ parameter can limit what should be linked.
# Options are :all (default), :email_addresses, and :urls. # Options are <tt>:all</tt> (default), <tt>:email_addresses</tt>, and <tt>:urls</tt>.
# #
# Example: # Example:
# auto_link("Go to http://www.rubyonrails.com and say hello to david@loudthinking.com") => # auto_link("Go to http://www.rubyonrails.com and say hello to david@loudthinking.com") =>
@ -235,28 +235,28 @@ module ActionView
# array every time it is called. This can be used to alternate classes # array every time it is called. This can be used to alternate classes
# for table rows: # for table rows:
# #
# <%- for item in @items do -%> # <%- for item in @items do -%>
# <tr class="<%= cycle("even", "odd") %>"> # <tr class="<%= cycle("even", "odd") %>">
# ... use item ... # ... use item ...
# </tr> # </tr>
# <%- end -%> # <%- end -%>
# #
# You can use named cycles to prevent clashes in nested loops. You'll # You can use named cycles to prevent clashes in nested loops. You'll
# have to reset the inner cycle, manually: # have to reset the inner cycle, manually:
# #
# <%- for item in @items do -%> # <%- for item in @items do -%>
# <tr class="<%= cycle("even", "odd", :name => "row_class") # <tr class="<%= cycle("even", "odd", :name => "row_class")
# <td> # <td>
# <%- for value in item.values do -%> # <%- for value in item.values do -%>
# <span style="color:'<%= cycle("red", "green", "blue" # <span style="color:'<%= cycle("red", "green", "blue"
# :name => "colors") %>'"> # :name => "colors") %>'">
# item # item
# </span> # </span>
# <%- end -%> # <%- end -%>
# <%- reset_cycle("colors") -%> # <%- reset_cycle("colors") -%>
# </td> # </td>
# </tr> # </tr>
# <%- end -%> # <%- end -%>
def cycle(first_value, *values) def cycle(first_value, *values)
if (values.last.instance_of? Hash) if (values.last.instance_of? Hash)
params = values.pop params = values.pop

View file

@ -46,8 +46,12 @@ Rake::RDocTask.new { |rdoc|
rdoc.title = "Action Pack -- On rails from request to response" rdoc.title = "Action Pack -- On rails from request to response"
rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '--line-numbers' << '--inline-source'
rdoc.template = "#{ENV['template']}.rb" if ENV['template'] rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG') if ENV['DOC_FILES']
rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
else
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
rdoc.rdoc_files.include('lib/**/*.rb')
end
} }
# Create compressed packages # Create compressed packages
@ -73,7 +77,7 @@ spec = Gem::Specification.new do |s|
s.require_path = 'lib' s.require_path = 'lib'
s.autorequire = 'action_controller' s.autorequire = 'action_controller'
s.files = [ "rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "MIT-LICENSE", "examples/.htaccess" ] s.files = [ "filler", "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "MIT-LICENSE", "examples/.htaccess" ]
dist_dirs.each do |dir| dist_dirs.each do |dir|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end end
@ -136,116 +140,12 @@ task :pdoc => [:rdoc] do
end end
desc "Publish the release files to RubyForge." desc "Publish the release files to RubyForge."
task :release => [:package] do task :release => [ :package ] do
files = ["gem", "tgz", "zip"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" } `rubyforge login`
if RUBY_FORGE_PROJECT then for ext in %w( gem tgz zip )
require 'net/http' release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
require 'open-uri' puts release_command
system(release_command)
project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
project_data = open(project_uri) { |data| data.read }
group_id = project_data[/[?&]group_id=(\d+)/, 1]
raise "Couldn't get group id" unless group_id
# This echos password to shell which is a bit sucky
if ENV["RUBY_FORGE_PASSWORD"]
password = ENV["RUBY_FORGE_PASSWORD"]
else
print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
password = STDIN.gets.chomp
end
login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
data = [
"login=1",
"form_loginname=#{RUBY_FORGE_USER}",
"form_pw=#{password}"
].join("&")
http.post("/account/login.php", data)
end
cookie = login_response["set-cookie"]
raise "Login failed" unless cookie
headers = { "Cookie" => cookie }
release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
release_data = open(release_uri, headers) { |data| data.read }
package_id = release_data[/[?&]package_id=(\d+)/, 1]
raise "Couldn't get package id" unless package_id
first_file = true
release_id = ""
files.each do |filename|
basename = File.basename(filename)
file_ext = File.extname(filename)
file_data = File.open(filename, "rb") { |file| file.read }
puts "Releasing #{basename}..."
release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
type_map = {
".zip" => "3000",
".tgz" => "3110",
".gz" => "3110",
".gem" => "1400"
}; type_map.default = "9999"
type = type_map[file_ext]
boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
query_hash = if first_file then
{
"group_id" => group_id,
"package_id" => package_id,
"release_name" => RELEASE_NAME,
"release_date" => release_date,
"type_id" => type,
"processor_id" => "8000", # Any
"release_notes" => "",
"release_changes" => "",
"preformatted" => "1",
"submit" => "1"
}
else
{
"group_id" => group_id,
"release_id" => release_id,
"package_id" => package_id,
"step2" => "1",
"type_id" => type,
"processor_id" => "8000", # Any
"submit" => "Add This File"
}
end
query = "?" + query_hash.map do |(name, value)|
[name, URI.encode(value)].join("=")
end.join("&")
data = [
"--" + boundary,
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
"Content-Type: application/octet-stream",
"Content-Transfer-Encoding: binary",
"", file_data, ""
].join("\x0D\x0A")
release_headers = headers.merge(
"Content-Type" => "multipart/form-data; boundary=#{boundary}"
)
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
http.post(target + query, data, release_headers)
end
if first_file then
release_id = release_response.body[/release_id=(\d+)/, 1]
raise("Couldn't get release id") unless release_id
end
first_file = false
end
end end
end end

View file

@ -6,6 +6,7 @@ class NotAController
end end
module Admin module Admin
class << self; alias_method :const_available?, :const_defined?; end class << self; alias_method :const_available?, :const_defined?; end
SomeConstant = 10
class UserController < Class.new(ActionController::Base); end class UserController < Class.new(ActionController::Base); end
class NewsFeedController < Class.new(ActionController::Base); end class NewsFeedController < Class.new(ActionController::Base); end
end end

View file

@ -0,0 +1,42 @@
require File.dirname(__FILE__) + '/../abstract_unit'
class FilterParamController < ActionController::Base
end
class FilterParamTest < Test::Unit::TestCase
def setup
@controller = FilterParamController.new
end
def test_filter_parameters
assert FilterParamController.respond_to?(:filter_parameter_logging)
assert !@controller.respond_to?(:filter_parameters)
FilterParamController.filter_parameter_logging
assert @controller.respond_to?(:filter_parameters)
test_hashes = [[{},{},[]],
[{'foo'=>'bar'},{'foo'=>'bar'},[]],
[{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
[{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'],
[{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'],
[{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
[{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
[{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana']]
test_hashes.each do |before_filter, after_filter, filter_words|
FilterParamController.filter_parameter_logging(*filter_words)
assert_equal after_filter, @controller.filter_parameters(before_filter)
filter_words.push('blah')
FilterParamController.filter_parameter_logging(*filter_words) do |key, value|
value.reverse! if key =~ /bargain/
end
before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
assert_equal after_filter, @controller.filter_parameters(before_filter)
end
end
end

View file

@ -535,7 +535,6 @@ end
class RouteTests < Test::Unit::TestCase class RouteTests < Test::Unit::TestCase
def route(*args) def route(*args)
@route = ::ActionController::Routing::Route.new(*args) unless args.empty? @route = ::ActionController::Routing::Route.new(*args) unless args.empty?
return @route return @route
@ -972,4 +971,79 @@ class RouteSetTests < Test::Unit::TestCase
end end
end end
class ControllerComponentTest < Test::Unit::TestCase
def test_traverse_to_controller_should_not_load_arbitrary_files
load_path = $:.dup
base = File.dirname(File.dirname(File.expand_path(__FILE__)))
$: << File.join(base, 'fixtures')
Object.send :const_set, :RAILS_ROOT, File.join(base, 'fixtures/application_root')
assert_equal nil, ActionController::Routing::ControllerComponent.traverse_to_controller(%w(dont_load pretty please))
ensure
$:[0..-1] = load_path
Object.send :remove_const, :RAILS_ROOT
end
def test_traverse_should_not_trip_on_non_module_constants
assert_equal nil, ActionController::Routing::ControllerComponent.traverse_to_controller(%w(admin some_constant a))
end
# This is evil, but people do it.
def test_traverse_to_controller_should_pass_thru_classes
load_path = $:.dup
base = File.dirname(File.dirname(File.expand_path(__FILE__)))
$: << File.join(base, 'fixtures')
$: << File.join(base, 'fixtures/application_root/app/controllers')
$: << File.join(base, 'fixtures/application_root/app/models')
Object.send :const_set, :RAILS_ROOT, File.join(base, 'fixtures/application_root')
pair = ActionController::Routing::ControllerComponent.traverse_to_controller(%w(a_class_that_contains_a_controller poorly_placed))
# Make sure the container class was loaded properly
assert defined?(AClassThatContainsAController)
assert_kind_of Class, AClassThatContainsAController
assert_equal :you_know_it, AClassThatContainsAController.is_special?
# Make sure the controller was too
assert_kind_of Array, pair
assert_equal 2, pair[1]
klass = pair.first
assert_kind_of Class, klass
assert_equal :decidedly_so, klass.is_evil?
assert klass.ancestors.include?(ActionController::Base)
assert defined?(AClassThatContainsAController::PoorlyPlacedController)
assert_equal klass, AClassThatContainsAController::PoorlyPlacedController
ensure
$:[0..-1] = load_path
Object.send :remove_const, :RAILS_ROOT
end
def test_traverse_to_nested_controller
load_path = $:.dup
base = File.dirname(File.dirname(File.expand_path(__FILE__)))
$: << File.join(base, 'fixtures')
$: << File.join(base, 'fixtures/application_root/app/controllers')
Object.send :const_set, :RAILS_ROOT, File.join(base, 'fixtures/application_root')
pair = ActionController::Routing::ControllerComponent.traverse_to_controller(%w(module_that_holds_controllers nested))
assert_not_equal nil, pair
# Make sure that we created a module for the dir
assert defined?(ModuleThatHoldsControllers)
assert_kind_of Module, ModuleThatHoldsControllers
# Make sure the controller is ok
assert_kind_of Array, pair
assert_equal 2, pair[1]
klass = pair.first
assert_kind_of Class, klass
assert klass.ancestors.include?(ActionController::Base)
assert defined?(ModuleThatHoldsControllers::NestedController)
assert_equal klass, ModuleThatHoldsControllers::NestedController
ensure
$:[0..-1] = load_path
Object.send :remove_const, :RAILS_ROOT
end
end
end end

View file

@ -92,4 +92,18 @@ class SendFileTest < Test::Unit::TestCase
h = @controller.headers h = @controller.headers
assert_equal 'private', h['Cache-Control'] assert_equal 'private', h['Cache-Control']
end end
%w(file data).each do |method|
define_method "test_send_#{method}_status" do
@controller.options = { :stream => false, :status => 500 }
assert_nothing_raised { assert_not_nil process(method) }
assert_equal '500', @controller.headers['Status']
end
define_method "test_default_send_#{method}_status" do
@controller.options = { :stream => false }
assert_nothing_raised { assert_not_nil process(method) }
assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @controller.headers['Status']
end
end
end end

View file

@ -0,0 +1,7 @@
class AClassThatContainsAController::PoorlyPlacedController < ActionController::Base
def self.is_evil?
:decidedly_so
end
end

View file

@ -0,0 +1,3 @@
class ModuleThatHoldsControllers::NestedController < ActionController::Base
end

View file

@ -0,0 +1,7 @@
class AClassThatContainsAController #often < ActiveRecord::Base
def self.is_special?
:you_know_it
end
end

View file

@ -0,0 +1,3 @@
# see routing/controller component tests
raise Exception, "I should never be loaded"

View file

@ -0,0 +1,134 @@
require 'test/unit'
require File.dirname(__FILE__) + '/../../lib/action_view/helpers/date_helper'
require File.dirname(__FILE__) + "/../abstract_unit"
class CompiledTemplateTests < Test::Unit::TestCase
def setup
@ct = ActionView::CompiledTemplates.new
@v = Class.new
@v.send :include, @ct
@a = './test_compile_template_a.rhtml'
@b = './test_compile_template_b.rhtml'
@s = './test_compile_template_link.rhtml'
end
def teardown
[@a, @b, @s].each do |f|
`rm #{f}` if File.exist?(f) || File.symlink?(f)
end
end
attr_reader :ct, :v
def test_name_allocation
hi_world = ct.method_names['hi world']
hi_sexy = ct.method_names['hi sexy']
wish_upon_a_star = ct.method_names['I love seeing decent error messages']
assert_equal hi_world, ct.method_names['hi world']
assert_equal hi_sexy, ct.method_names['hi sexy']
assert_equal wish_upon_a_star, ct.method_names['I love seeing decent error messages']
assert_equal 3, [hi_world, hi_sexy, wish_upon_a_star].uniq.length
end
def test_wrap_source
assert_equal(
"def aliased_assignment(value)\nself.value = value\nend",
@ct.wrap_source(:aliased_assignment, [:value], 'self.value = value')
)
assert_equal(
"def simple()\nnil\nend",
@ct.wrap_source(:simple, [], 'nil')
)
end
def test_compile_source_single_method
selector = ct.compile_source('doubling method', [:a], 'a + a')
assert_equal 2, @v.new.send(selector, 1)
assert_equal 4, @v.new.send(selector, 2)
assert_equal -4, @v.new.send(selector, -2)
assert_equal 0, @v.new.send(selector, 0)
selector
end
def test_compile_source_two_method
sel1 = test_compile_source_single_method # compile the method in the other test
sel2 = ct.compile_source('doubling method', [:a, :b], 'a + b + a + b')
assert_not_equal sel1, sel2
assert_equal 2, @v.new.send(sel1, 1)
assert_equal 4, @v.new.send(sel1, 2)
assert_equal 6, @v.new.send(sel2, 1, 2)
assert_equal 32, @v.new.send(sel2, 15, 1)
end
def test_mtime
t1 = Time.now
test_compile_source_single_method
assert (t1..Time.now).include?(ct.mtime('doubling method', [:a]))
end
def test_compile_time
`echo '#{@a}' > #{@a}; echo '#{@b}' > #{@b}; ln -s #{@a} #{@s}`
v = ActionView::Base.new
v.base_path = '.'
v.cache_template_loading = false;
sleep 1
t = Time.now
v.compile_and_render_template(:rhtml, '', @a)
v.compile_and_render_template(:rhtml, '', @b)
v.compile_and_render_template(:rhtml, '', @s)
a_n = v.method_names[@a]
b_n = v.method_names[@b]
s_n = v.method_names[@s]
# all of the files have changed since last compile
assert v.compile_time[a_n] > t
assert v.compile_time[b_n] > t
assert v.compile_time[s_n] > t
sleep 1
t = Time.now
v.compile_and_render_template(:rhtml, '', @a)
v.compile_and_render_template(:rhtml, '', @b)
v.compile_and_render_template(:rhtml, '', @s)
# none of the files have changed since last compile
assert v.compile_time[a_n] < t
assert v.compile_time[b_n] < t
assert v.compile_time[s_n] < t
`rm #{@s}; ln -s #{@b} #{@s}`
v.compile_and_render_template(:rhtml, '', @a)
v.compile_and_render_template(:rhtml, '', @b)
v.compile_and_render_template(:rhtml, '', @s)
# the symlink has changed since last compile
assert v.compile_time[a_n] < t
assert v.compile_time[b_n] < t
assert v.compile_time[s_n] > t
sleep 1
`touch #{@b}`
t = Time.now
v.compile_and_render_template(:rhtml, '', @a)
v.compile_and_render_template(:rhtml, '', @b)
v.compile_and_render_template(:rhtml, '', @s)
# the file at the end of the symlink has changed since last compile
# both the symlink and the file at the end of it should be recompiled
assert v.compile_time[a_n] < t
assert v.compile_time[b_n] > t
assert v.compile_time[s_n] > t
end
end
module ActionView
class Base
def compile_time
@@compile_time
end
def method_names
@@method_names
end
end
end

View file

@ -1,3 +1,18 @@
*1.1.5* (August 8th, 2006)
* Rely on Action Pack 1.12.4 and Active Record 1.14.4
*1.1.4* (June 29th, 2006)
* Rely on Action Pack 1.12.3
*1.1.3* (June 27th, 2006)
* Rely on Action Pack 1.12.2 and Active Record 1.14.3
*1.1.2* (April 9th, 2005) *1.1.2* (April 9th, 2005)
* Rely on Active Record 1.14.2 * Rely on Active Record 1.14.2

View file

@ -71,8 +71,8 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "aws" s.rubyforge_project = "aws"
s.homepage = "http://www.rubyonrails.org" s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 1.12.1' + PKG_BUILD) s.add_dependency('actionpack', '= 1.12.4' + PKG_BUILD)
s.add_dependency('activerecord', '= 1.14.2' + PKG_BUILD) s.add_dependency('activerecord', '= 1.14.4' + PKG_BUILD)
s.has_rdoc = true s.has_rdoc = true
s.requirements << 'none' s.requirements << 'none'

View file

@ -2,7 +2,7 @@ module ActionWebService
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 1 MAJOR = 1
MINOR = 1 MINOR = 1
TINY = 2 TINY = 5
STRING = [MAJOR, MINOR, TINY].join('.') STRING = [MAJOR, MINOR, TINY].join('.')
end end

View file

@ -1,9 +1,31 @@
*1.14.2* (April 9th, 2005) *1.14.4* (August 8th, 2006)
* Add warning about the proper way to validate the presence of a foreign key. #4147 [Francois Beausoleil <francois.beausoleil@gmail.com>]
* Fix syntax error in documentation. #4679 [mislav@nippur.irb.hr]
* Update inconsistent migrations documentation. #4683 [machomagna@gmail.com]
*1.14.3* (June 27th, 2006)
* Fix announcement of very long migration names. #5722 [blake@near-time.com]
* Update callbacks documentation. #3970 [Robby Russell <robby@planetargon.com>]
* Properly quote index names in migrations (closes #4764) [John Long]
* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
*1.14.2* (April 9th, 2006)
* Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen] * Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen]
*1.14.1* (April 6th, 2005) *1.14.1* (April 6th, 2006)
* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar] * Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
@ -58,7 +80,7 @@
* Fixed broken OCIAdapter #4457 [schoenm@earthlink.net] * Fixed broken OCIAdapter #4457 [schoenm@earthlink.net]
*1.14.0* (March 27th, 2005) *1.14.0* (March 27th, 2006)
* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar] * Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
@ -478,6 +500,7 @@
* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Luetke] * Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Luetke]
*1.13.2* (December 13th, 2005) *1.13.2* (December 13th, 2005)
* Become part of Rails 1.0 * Become part of Rails 1.0

View file

@ -173,8 +173,8 @@ desc "Publish the release files to RubyForge."
task :release => [ :package ] do task :release => [ :package ] do
`rubyforge login` `rubyforge login`
for ext in %w( gem ) for ext in %w( gem tgz zip )
release_command = "rubyforge add_release activerecord activerecord 'REL #{PKG_VERSION}' pkg/activerecord-#{PKG_VERSION}.#{ext}" release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
puts release_command puts release_command
system(release_command) system(release_command)
end end

View file

@ -1163,18 +1163,19 @@ module ActiveRecord
end end
def select_limited_ids_list(options, join_dependency) def select_limited_ids_list(options, join_dependency)
connection.select_values( connection.select_all(
construct_finder_sql_for_association_limiting(options, join_dependency), construct_finder_sql_for_association_limiting(options, join_dependency),
"#{name} Load IDs For Limited Eager Loading" "#{name} Load IDs For Limited Eager Loading"
).collect { |id| connection.quote(id) }.join(", ") ).collect { |row| connection.quote(row[primary_key]) }.join(", ")
end end
def construct_finder_sql_for_association_limiting(options, join_dependency) def construct_finder_sql_for_association_limiting(options, join_dependency)
scope = scope(:find) scope = scope(:find)
#sql = "SELECT DISTINCT #{table_name}.#{primary_key} FROM #{table_name} "
sql = "SELECT " sql = "SELECT "
sql << "DISTINCT #{table_name}." if include_eager_conditions?(options) || include_eager_order?(options) sql << "DISTINCT #{table_name}." if include_eager_conditions?(options) || include_eager_order?(options)
sql << "#{primary_key} FROM #{table_name} " sql << primary_key
sql << ", #{options[:order].split(',').collect { |s| s.split.first } * ', '}" if options[:order] && (include_eager_conditions?(options) || include_eager_order?(options))
sql << " FROM #{table_name} "
if include_eager_conditions?(options) || include_eager_order?(options) if include_eager_conditions?(options) || include_eager_order?(options)
sql << join_dependency.join_associations.collect{|join| join.association_join }.join sql << join_dependency.join_associations.collect{|join| join.association_join }.join
@ -1187,15 +1188,23 @@ module ActiveRecord
return sanitize_sql(sql) return sanitize_sql(sql)
end end
# Checks if the conditions reference a table other than the current model table
def include_eager_conditions?(options) def include_eager_conditions?(options)
conditions = scope(:find, :conditions) || options[:conditions] # look in both sets of conditions
return false unless conditions conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
conditions = conditions.first if conditions.is_a?(Array) case cond
conditions.scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name| when nil then all
when Array then all << cond.first
else all << cond
end
end
return false unless conditions.any?
conditions.join(' ').scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name|
condition_table_name != table_name condition_table_name != table_name
end end
end end
# Checks if the query order references a table other than the current model's table.
def include_eager_order?(options) def include_eager_order?(options)
order = options[:order] order = options[:order]
return false unless order return false unless order

View file

@ -175,7 +175,7 @@ module ActiveRecord #:nodoc:
# serialize :preferences # serialize :preferences
# end # end
# #
# user = User.create(:preferences) => { "background" => "black", "display" => large }) # user = User.create(:preferences => { "background" => "black", "display" => large })
# User.find(user.id).preferences # => { "background" => "black", "display" => large } # User.find(user.id).preferences # => { "background" => "black", "display" => large }
# #
# You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a

View file

@ -42,26 +42,29 @@ module ActiveRecord
# #
# Note: Person.count(:all) will not work because it will use :all as the condition. Use Person.count instead. # Note: Person.count(:all) will not work because it will use :all as the condition. Use Person.count instead.
def count(*args) def count(*args)
options = {} options = {}
column_name = :all
#For backwards compatibility, we need to handle both count(conditions=nil, joins=nil) or count(options={}). # For backwards compatibility, we need to handle both count(conditions=nil, joins=nil) or count(options={}) or count(column_name=:all, options={}).
if args.size >= 0 and args.size <= 2 if args.size >= 0 && args.size <= 2
if args.first.is_a?(Hash) if args.first.is_a?(Hash)
options = args.first options = args.first
#should we verify the options hash???
elsif args[1].is_a?(Hash) elsif args[1].is_a?(Hash)
options = args[1]
column_name = args.first column_name = args.first
options = args[1]
else else
# Handle legacy paramter options: def count(conditions=nil, joins=nil) # Handle legacy paramter options: def count(conditions=nil, joins=nil)
options.merge!(:conditions => args[0]) if args.length > 0 options.merge!(:conditions => args[0]) if args.length > 0
options.merge!(:joins => args[1]) if args.length > 1 options.merge!(:joins => args[1]) if args.length > 1
end end
else else
raise(ArgumentError, "Unexpected parameters passed to count(*args): expected either count(conditions=nil, joins=nil) or count(options={})") raise(ArgumentError, "Unexpected parameters passed to count(*args): expected either count(conditions=nil, joins=nil) or count(options={})")
end end
(scope(:find, :include) || options[:include]) ? count_with_associations(options) : calculate(:count, :all, options) if options[:include] || scope(:find, :include)
count_with_associations(options)
else
calculate(:count, column_name, options)
end
end end
# Calculates average value on a given column. The value is returned as a float. See #calculate for examples with options. # Calculates average value on a given column. The value is returned as a float. See #calculate for examples with options.

View file

@ -243,6 +243,10 @@ module ActiveRecord
def before_save() end def before_save() end
# Is called _after_ Base.save (regardless of whether it's a create or update save). # Is called _after_ Base.save (regardless of whether it's a create or update save).
#
# class Contact < ActiveRecord::Base
# after_save { logger.info( 'New contact saved!' ) }
# end
def after_save() end def after_save() end
def create_or_update_with_callbacks #:nodoc: def create_or_update_with_callbacks #:nodoc:
return false if callback(:before_save) == false return false if callback(:before_save) == false
@ -312,9 +316,16 @@ module ActiveRecord
end end
# Is called _before_ Base.destroy. # Is called _before_ Base.destroy.
#
# Note: If you need to _destroy_ or _nullify_ associated records first,
# use the _:dependent_ option on your associations.
def before_destroy() end def before_destroy() end
# Is called _after_ Base.destroy (and all the attributes have been frozen). # Is called _after_ Base.destroy (and all the attributes have been frozen).
#
# class Contact < ActiveRecord::Base
# after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
# end
def after_destroy() end def after_destroy() end
def destroy_with_callbacks #:nodoc: def destroy_with_callbacks #:nodoc:
return false if callback(:before_destroy) == false return false if callback(:before_destroy) == false

View file

@ -119,7 +119,7 @@ module ActiveRecord
# Adds a new column to the named table. # Adds a new column to the named table.
# See TableDefinition#column for details of the options you can use. # See TableDefinition#column for details of the options you can use.
def add_column(table_name, column_name, type, options = {}) def add_column(table_name, column_name, type, options = {})
add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}" add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
add_column_options!(add_column_sql, options) add_column_options!(add_column_sql, options)
execute(add_column_sql) execute(add_column_sql)
end end
@ -128,7 +128,7 @@ module ActiveRecord
# ===== Examples # ===== Examples
# remove_column(:suppliers, :qualification) # remove_column(:suppliers, :qualification)
def remove_column(table_name, column_name) def remove_column(table_name, column_name)
execute "ALTER TABLE #{table_name} DROP #{column_name}" execute "ALTER TABLE #{table_name} DROP #{quote_column_name(column_name)}"
end end
# Changes the column's definition according to the new options. # Changes the column's definition according to the new options.
@ -184,7 +184,8 @@ module ActiveRecord
# generates # generates
# CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id) # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
def add_index(table_name, column_name, options = {}) def add_index(table_name, column_name, options = {})
index_name = "#{table_name}_#{Array(column_name).first}_index" column_names = Array(column_name)
index_name = index_name(table_name, :column => column_names.first)
if Hash === options # legacy support, since this param was a string if Hash === options # legacy support, since this param was a string
index_type = options[:unique] ? "UNIQUE" : "" index_type = options[:unique] ? "UNIQUE" : ""
@ -192,8 +193,8 @@ module ActiveRecord
else else
index_type = options index_type = options
end end
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{Array(column_name).join(", ")})" execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{table_name} (#{quoted_column_names})"
end end
# Remove the given index from the table. # Remove the given index from the table.
@ -209,7 +210,7 @@ module ActiveRecord
# add_index :accounts, [:username, :password] # add_index :accounts, [:username, :password]
# remove_index :accounts, :username # remove_index :accounts, :username
def remove_index(table_name, options = {}) def remove_index(table_name, options = {})
execute "DROP INDEX #{index_name(table_name, options)} ON #{table_name}" execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} ON #{table_name}"
end end
def index_name(table_name, options) #:nodoc: def index_name(table_name, options) #:nodoc:

View file

@ -18,7 +18,6 @@ module ActiveRecord
end end
end end
config = config.symbolize_keys config = config.symbolize_keys
host = config[:host] host = config[:host]
port = config[:port] port = config[:port]

View file

@ -339,7 +339,6 @@ module ActiveRecord
execute "DROP INDEX #{index_name(table_name, options)}" execute "DROP INDEX #{index_name(table_name, options)}"
end end
private private
BYTEA_COLUMN_TYPE_OID = 17 BYTEA_COLUMN_TYPE_OID = 17
TIMESTAMPOID = 1114 TIMESTAMPOID = 1114

View file

@ -213,13 +213,7 @@ module ActiveRecord
end end
def remove_index(table_name, options={}) #:nodoc: def remove_index(table_name, options={}) #:nodoc:
if Hash === options execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
index_name = options[:name]
else
index_name = "#{table_name}_#{options}_index"
end
execute "DROP INDEX #{index_name}"
end end
def rename_table(name, new_name) def rename_table(name, new_name)

View file

@ -70,8 +70,8 @@ module ActiveRecord
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes the column to a different type using the same # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes the column to a different type using the same
# parameters as add_column. # parameters as add_column.
# * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+. # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+.
# * <tt>add_index(table_name, column_name, index_type)</tt>: Add a new index with the name of the column on the column. Specify an optional index_type (e.g. UNIQUE). # * <tt>add_index(table_name, column_names, index_type, index_name)</tt>: Add a new index with the name of the column, or +index_name+ (if specified) on the column(s). Specify an optional +index_type+ (e.g. UNIQUE).
# * <tt>remove_index(table_name, column_name)</tt>: Remove the index called the same as the column. # * <tt>remove_index(table_name, index_name)</tt>: Remove the index specified by +index_name+.
# #
# == Irreversible transformations # == Irreversible transformations
# #
@ -243,7 +243,8 @@ module ActiveRecord
def announce(message) def announce(message)
text = "#{name}: #{message}" text = "#{name}: #{message}"
write "== %s %s" % [ text, "=" * (75 - text.length) ] length = [0, 75 - text.length].max
write "== %s %s" % [text, "=" * length]
end end
def say(message, subitem=false) def say(message, subitem=false)

View file

@ -381,6 +381,18 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
# method, proc or string should return or evaluate to a true or false value. # method, proc or string should return or evaluate to a true or false value.
#
# === Warning
# Validate the presence of the foreign key, not the instance variable itself.
# Do this:
# validate_presence_of :invoice_id
#
# Not this:
# validate_presence_of :invoice
#
# If you validate the presence of the associated object, you will get
# failures on saves when both the parent object and the child object are
# new.
def validates_presence_of(*attr_names) def validates_presence_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save } configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)

View file

@ -2,7 +2,7 @@ module ActiveRecord
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 1 MAJOR = 1
MINOR = 14 MINOR = 14
TINY = 2 TINY = 4
STRING = [MAJOR, MINOR, TINY].join('.') STRING = [MAJOR, MINOR, TINY].join('.')
end end

View file

@ -179,6 +179,42 @@ class EagerAssociationTest < Test::Unit::TestCase
assert_equal count, posts.size assert_equal count, posts.size
end end
def test_eager_with_has_many_and_limit_and_scoped_conditions_on_the_eagers
posts = nil
Post.with_scope(:find => {
:include => :comments,
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
}) do
posts = authors(:david).posts.find(:all, :limit => 2)
assert_equal 2, posts.size
end
Post.with_scope(:find => {
:include => [ :comments, :author ],
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
}) do
count = Post.count(:limit => 2)
assert_equal count, posts.size
end
end
def test_eager_with_has_many_and_limit_and_scoped_and_explicit_conditions_on_the_eagers
Post.with_scope(:find => { :conditions => "1=1" }) do
posts = authors(:david).posts.find(:all,
:include => :comments,
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
:limit => 2
)
assert_equal 2, posts.size
count = Post.count(
:include => [ :comments, :author ],
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
:limit => 2
)
assert_equal count, posts.size
end
end
def test_eager_association_loading_with_habtm def test_eager_association_loading_with_habtm
posts = Post.find(:all, :include => :categories, :order => "posts.id") posts = Post.find(:all, :include => :categories, :order => "posts.id")
assert_equal 2, posts[0].categories.size assert_equal 2, posts[0].categories.size

View file

@ -922,6 +922,16 @@ class BasicsTest < Test::Unit::TestCase
assert_equal("<baz>", inverted["quux"]) assert_equal("<baz>", inverted["quux"])
end end
def test_sql_injection_via_find
assert_raises(ActiveRecord::RecordNotFound) do
Topic.find("123456 OR id > 0")
end
assert_raises(ActiveRecord::RecordNotFound) do
Topic.find(";;; this should raise an RecordNotFound error")
end
end
def test_column_name_properly_quoted def test_column_name_properly_quoted
col_record = ColumnName.new col_record = ColumnName.new
col_record.references = 40 col_record.references = 40

View file

@ -34,6 +34,7 @@ if ActiveRecord::Base.connection.supports_migrations?
Reminder.reset_column_information Reminder.reset_column_information
Person.connection.remove_column("people", "last_name") rescue nil Person.connection.remove_column("people", "last_name") rescue nil
Person.connection.remove_column("people", "key") rescue nil
Person.connection.remove_column("people", "bio") rescue nil Person.connection.remove_column("people", "bio") rescue nil
Person.connection.remove_column("people", "age") rescue nil Person.connection.remove_column("people", "age") rescue nil
Person.connection.remove_column("people", "height") rescue nil Person.connection.remove_column("people", "height") rescue nil
@ -47,6 +48,7 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_add_index def test_add_index
Person.connection.add_column "people", "last_name", :string Person.connection.add_column "people", "last_name", :string
Person.connection.add_column "people", "administrator", :boolean Person.connection.add_column "people", "administrator", :boolean
Person.connection.add_column "people", "key", :string
assert_nothing_raised { Person.connection.add_index("people", "last_name") } assert_nothing_raised { Person.connection.add_index("people", "last_name") }
assert_nothing_raised { Person.connection.remove_index("people", "last_name") } assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
@ -54,6 +56,10 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", "last_name") } assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
# quoting
assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key", :unique => true) }
assert_nothing_raised { Person.connection.remove_index("people", :name => "key") }
# Sybase adapter does not support indexes on :boolean columns # Sybase adapter does not support indexes on :boolean columns
unless current_adapter?(:SybaseAdapter) unless current_adapter?(:SybaseAdapter)
assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") } assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }

View file

@ -1,11 +1,32 @@
*1.1.2* (April 9th, 2005) *1.1.5* (August 8th, 2006)
* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. #4857 [Alisdair McDiarmid]
* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.]
* Includes critical security patch
*1.1.4* (June 29th, 2006)
* Remove use of opts.on { |options[:name] } style hash assignment. References #4440. [headius@headius.com]
* Updated to Action Pack 1.12.3, ActionWebService 1.1.4, ActionMailer 1.2.3
*1.1.3* (June 27th, 2006)
* Updated to Active Record 1.14.3, Action Pack 1.12.2, ActionWebService 1.1.3, ActionMailer 1.2.2
*1.1.2* (April 9th, 2006)
* Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) [DHH] * Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) [DHH]
* Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem [DHH] * Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem [DHH]
*1.1.1* (April 6th, 2005) *1.1.1* (April 6th, 2006)
* Enhances plugin#discover allowing it to discover svn:// like URIs (closes #4565) [ruben.nine@gmail.com] * Enhances plugin#discover allowing it to discover svn:// like URIs (closes #4565) [ruben.nine@gmail.com]
@ -44,7 +65,7 @@
* Avoid passing escapeHTML non-string in Rails' info controller [Nicholas Seckar] * Avoid passing escapeHTML non-string in Rails' info controller [Nicholas Seckar]
*1.1.0* (March 27th, 2005) *1.1.0* (March 27th, 2006)
* Allow db:fixtures:load to load a subset of the applications fixtures. [Chad Fowler] * Allow db:fixtures:load to load a subset of the applications fixtures. [Chad Fowler]
@ -185,6 +206,7 @@
* Honor ActiveRecord::Base.pluralize_table_names when creating and destroying session store table. #3204. [rails@bencurtis.com, Marcel Molina Jr.] * Honor ActiveRecord::Base.pluralize_table_names when creating and destroying session store table. #3204. [rails@bencurtis.com, Marcel Molina Jr.]
*1.0.0* (December 13th, 2005) *1.0.0* (December 13th, 2005)
* Update instructions on how to find and install generators. #3172. [Chad Fowler] * Update instructions on how to find and install generators. #3172. [Chad Fowler]

View file

@ -124,6 +124,9 @@ application is running. You can inspect domain models, change values, and save t
database. Starting the script without arguments will launch it in the development environment. database. Starting the script without arguments will launch it in the development environment.
Passing an argument will specify a different environment, like <tt>script/console production</tt>. Passing an argument will specify a different environment, like <tt>script/console production</tt>.
To reload your controllers and models after launching the console run <tt>reload!</tt>
== Description of contents == Description of contents

View file

@ -279,10 +279,10 @@ spec = Gem::Specification.new do |s|
s.add_dependency('rake', '>= 0.7.1') s.add_dependency('rake', '>= 0.7.1')
s.add_dependency('activesupport', '= 1.3.1' + PKG_BUILD) s.add_dependency('activesupport', '= 1.3.1' + PKG_BUILD)
s.add_dependency('activerecord', '= 1.14.2' + PKG_BUILD) s.add_dependency('activerecord', '= 1.14.4' + PKG_BUILD)
s.add_dependency('actionpack', '= 1.12.1' + PKG_BUILD) s.add_dependency('actionpack', '= 1.12.4' + PKG_BUILD)
s.add_dependency('actionmailer', '= 1.2.1' + PKG_BUILD) s.add_dependency('actionmailer', '= 1.2.4' + PKG_BUILD)
s.add_dependency('actionwebservice', '= 1.1.2' + PKG_BUILD) s.add_dependency('actionwebservice', '= 1.1.5' + PKG_BUILD)
s.rdoc_options << '--exclude' << '.' s.rdoc_options << '--exclude' << '.'
s.has_rdoc = false s.has_rdoc = false

View file

@ -13,7 +13,7 @@ require File.join(File.dirname(__FILE__), 'boot')
Rails::Initializer.run do |config| Rails::Initializer.run do |config|
# Settings in config/environments/* take precedence those specified here # Settings in config/environments/* take precedence those specified here
# Skip frameworks you're not going to use # Skip frameworks you're not going to use (only works if using vendor/rails)
# config.frameworks -= [ :action_web_service, :action_mailer ] # config.frameworks -= [ :action_web_service, :action_mailer ]
# Add additional load paths for your own custom dirs # Add additional load paths for your own custom dirs

View file

@ -4,8 +4,8 @@ require 'optparse'
options = { :sandbox => false, :irb => irb } options = { :sandbox => false, :irb => irb }
OptionParser.new do |opt| OptionParser.new do |opt|
opt.banner = "Usage: console [environment] [options]" opt.banner = "Usage: console [environment] [options]"
opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |options[:sandbox]| } opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |options[:irb]| } opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v }
opt.parse!(ARGV) opt.parse!(ARGV)
end end

View file

@ -117,8 +117,8 @@ ARGV.options do |opts|
opts.on(" Options:") opts.on(" Options:")
opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |OPTIONS[:action]| } opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |v| OPTIONS[:action] = v }
opts.on("-d", "--dispatcher=path", "default: #{OPTIONS[:dispatcher]}", String) { |OPTIONS[:dispatcher]| } opts.on("-d", "--dispatcher=path", "default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v }
opts.separator "" opts.separator ""

View file

@ -65,11 +65,11 @@ ARGV.options do |opts|
opts.on(" Options:") opts.on(" Options:")
opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |OPTIONS[:port]| } opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v }
opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |OPTIONS[:instances]| } opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |v| OPTIONS[:instances] = v }
opts.on("-r", "--repeat=seconds", Integer, "Repeat spawn attempts every n seconds (default: off)") { |OPTIONS[:repeat]| } opts.on("-r", "--repeat=seconds", Integer, "Repeat spawn attempts every n seconds (default: off)") { |v| OPTIONS[:repeat] = v }
opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |OPTIONS[:environment]| } opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |v| OPTIONS[:environment] = v }
opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |OPTIONS[:spawner]| } opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |v| OPTIONS[:spawner] = v }
opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) } opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) }
opts.separator "" opts.separator ""

View file

@ -36,9 +36,9 @@ ARGV.options do |opts|
opts.on(" Options:") opts.on(" Options:")
opts.on("-c", "--command=path", String) { |OPTIONS[:command]| } opts.on("-c", "--command=path", String) { |v| OPTIONS[:command] = v }
opts.on("-i", "--interval=seconds", Float) { |OPTIONS[:interval]| } opts.on("-i", "--interval=seconds", Float) { |v| OPTIONS[:interval] = v }
opts.on("-d", "--daemon") { |OPTIONS[:daemon]| } opts.on("-d", "--daemon") { |v| OPTIONS[:daemon] = v }
opts.separator "" opts.separator ""

View file

@ -10,7 +10,7 @@ ARGV.options do |opts|
opts.on("-e", "--environment=name", String, opts.on("-e", "--environment=name", String,
"Specifies the environment for the runner to operate under (test/development/production).", "Specifies the environment for the runner to operate under (test/development/production).",
"Default: development") { |options[:environment]| } "Default: development") { |v| options[:environment] = v }
opts.separator "" opts.separator ""

View file

@ -19,13 +19,13 @@ ARGV.options do |opts|
opts.on("-p", "--port=port", Integer, opts.on("-p", "--port=port", Integer,
"Runs Rails on the specified port.", "Runs Rails on the specified port.",
"Default: 3000") { |OPTIONS[:port]| } "Default: 3000") { |v| OPTIONS[:port] = v }
opts.on("-b", "--binding=ip", String, opts.on("-b", "--binding=ip", String,
"Binds Rails to the specified ip.", "Binds Rails to the specified ip.",
"Default: 0.0.0.0") { |OPTIONS[:ip]| } "Default: 0.0.0.0") { |v| OPTIONS[:ip] = v }
opts.on("-e", "--environment=name", String, opts.on("-e", "--environment=name", String,
"Specifies the environment to run this server under (test/development/production).", "Specifies the environment to run this server under (test/development/production).",
"Default: development") { |OPTIONS[:environment]| } "Default: development") { |v| OPTIONS[:environment] = v }
opts.on("-m", "--mime-types=filename", String, opts.on("-m", "--mime-types=filename", String,
"Specifies an Apache style mime.types configuration file to be used for mime types", "Specifies an Apache style mime.types configuration file to be used for mime types",
"Default: none") { |mime_types_file| OPTIONS[:mime_types] = WEBrick::HTTPUtils::load_mime_types(mime_types_file) } "Default: none") { |mime_types_file| OPTIONS[:mime_types] = WEBrick::HTTPUtils::load_mime_types(mime_types_file) }
@ -36,7 +36,7 @@ ARGV.options do |opts|
opts.on("-c", "--charset=charset", String, opts.on("-c", "--charset=charset", String,
"Set default charset for output.", "Set default charset for output.",
"Default: UTF-8") { |OPTIONS[:charset]| } "Default: UTF-8") { |v| OPTIONS[:charset] = v }
opts.separator "" opts.separator ""

View file

@ -2,7 +2,7 @@ module Rails
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 1 MAJOR = 1
MINOR = 1 MINOR = 1
TINY = 2 TINY = 5
STRING = [MAJOR, MINOR, TINY].join('.') STRING = [MAJOR, MINOR, TINY].join('.')
end end

View file

@ -96,15 +96,15 @@ class AppGenerator < Rails::Generator::Base
opt.separator 'Options:' opt.separator 'Options:'
opt.on("-r", "--ruby=path", String, opt.on("-r", "--ruby=path", String,
"Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).", "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
"Default: #{DEFAULT_SHEBANG}") { |options[:shebang]| } "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
opt.on("-d", "--database=name", String, opt.on("-d", "--database=name", String,
"Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3).", "Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3).",
"Default: mysql") { |options[:db]| } "Default: mysql") { |v| options[:db] = v }
opt.on("-f", "--freeze", opt.on("-f", "--freeze",
"Freeze Rails in vendor/rails from the gems generating the skeleton", "Freeze Rails in vendor/rails from the gems generating the skeleton",
"Default: false") { |options[:freeze]| } "Default: false") { |v| options[:freeze] = v }
end end
def mysql_socket_location def mysql_socket_location

View file

@ -29,6 +29,6 @@ class ModelGenerator < Rails::Generator::NamedBase
opt.separator '' opt.separator ''
opt.separator 'Options:' opt.separator 'Options:'
opt.on("--skip-migration", opt.on("--skip-migration",
"Don't generate a migration file for this model") { |options[:skip_migration]| } "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
end end
end end

View file

@ -7,7 +7,7 @@
<p style="color: green"><%%= flash[:notice] %></p> <p style="color: green"><%%= flash[:notice] %></p>
<%%= @content_for_layout %> <%%= yield %>
</body> </body>
</html> </html>

View file

@ -121,12 +121,12 @@ module Rails
opt.separator '' opt.separator ''
opt.separator 'General Options:' opt.separator 'General Options:'
opt.on('-p', '--pretend', 'Run but do not make any changes.') { |options[:pretend]| } opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v }
opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force } opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force }
opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip } opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip }
opt.on('-q', '--quiet', 'Suppress normal output.') { |options[:quiet]| } opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v }
opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |options[:backtrace]| } opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v }
opt.on('-h', '--help', 'Show this help message.') { |options[:help]| } opt.on('-h', '--help', 'Show this help message.') { |v| options[:help] = v }
opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do
options[:svn] = `svn status`.inject({}) do |opt, e| options[:svn] = `svn status`.inject({}) do |opt, e|
opt[e.chomp[7..-1]] = true opt[e.chomp[7..-1]] = true

View file

@ -38,7 +38,7 @@ module Rails
protected protected
# Override with your own script usage banner. # Override with your own script usage banner.
def banner def banner
"Usage: #{$0} [options] generator [args]" "Usage: #{$0} generator [options] [args]"
end end
def usage_message def usage_message