Merge branch 'master' of git@github.com:rails/rails

This commit is contained in:
Jeremy Kemper 2009-02-05 23:05:41 -08:00
commit 34f34e3009
149 changed files with 18803 additions and 60 deletions

1
.gitignore vendored

@ -14,6 +14,7 @@ railties/pkg
railties/test/500.html railties/test/500.html
railties/doc/guides/html/images railties/doc/guides/html/images
railties/doc/guides/html/stylesheets railties/doc/guides/html/stylesheets
railties/guides/output
*.rbc *.rbc
*.swp *.swp
*.swo *.swo

@ -1,3 +1,8 @@
*Edge*
* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]
*2.3.0 [RC1] (February 1st, 2009)* *2.3.0 [RC1] (February 1st, 2009)*
* Fixed RFC-2045 quoted-printable bug #1421 [squadette] * Fixed RFC-2045 quoted-printable bug #1421 [squadette]

@ -672,7 +672,7 @@ def create_mail
def perform_delivery_smtp(mail) def perform_delivery_smtp(mail)
destinations = mail.destinations destinations = mail.destinations
mail.ready_to_send mail.ready_to_send
sender = mail['return-path'] || mail.from sender = (mail['return-path'] && mail['return-path'].spec) || mail.from
smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port]) smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto) smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto)

@ -938,6 +938,7 @@ def test_return_path_with_deliver
ActionMailer::Base.delivery_method = :smtp ActionMailer::Base.delivery_method = :smtp
TestMailer.deliver_return_path TestMailer.deliver_return_path
assert_match %r{^Return-Path: <another@somewhere.test>}, MockSMTP.deliveries[0][0] assert_match %r{^Return-Path: <another@somewhere.test>}, MockSMTP.deliveries[0][0]
assert_equal "another@somewhere.test", MockSMTP.deliveries[0][1].to_s
end end
def test_body_is_stored_as_an_ivar def test_body_is_stored_as_an_ivar

@ -1,5 +1,7 @@
*Edge* *Edge*
* Fix a syntax error in current_page?() that was prevent matches against URL's with multiple query parameters #1385, #1868 [chris finne/Andrew White]
* Added localized rescue template when I18n.locale is set (ex: public/404.da.html) #1835 [José Valim] * Added localized rescue template when I18n.locale is set (ex: public/404.da.html) #1835 [José Valim]

@ -7,7 +7,6 @@ def define_dispatcher_callbacks(cache_classes)
unless cache_classes unless cache_classes
# Development mode callbacks # Development mode callbacks
before_dispatch :reload_application before_dispatch :reload_application
after_dispatch :cleanup_application
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end end
@ -93,11 +92,9 @@ def reload_application
run_callbacks :prepare_dispatch run_callbacks :prepare_dispatch
Routing::Routes.reload Routing::Routes.reload
end
# Cleanup the application by clearing out loaded classes so they can # Cleanup the application by clearing out loaded classes so they can
# be reloaded on the next request without restarting the server. # be reloaded on the next request without restarting the server.
def cleanup_application
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
ActiveSupport::Dependencies.clear ActiveSupport::Dependencies.clear
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord) ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)

@ -173,7 +173,7 @@ def layout_conditions #:nodoc:
end end
def default_layout(format) #:nodoc: def default_layout(format) #:nodoc:
layout = read_inheritable_attribute(:layout) layout = read_inheritable_attribute(:layout) unless format == :js
return layout unless read_inheritable_attribute(:auto_layout) return layout unless read_inheritable_attribute(:auto_layout)
find_layout(layout, format) find_layout(layout, format)
end end

@ -58,9 +58,28 @@ def loaded?
end end
def load! def load!
@id, session = @by.send(:load_session, @env) stale_session_check! do
replace(session) @id, session = @by.send(:load_session, @env)
@loaded = true replace(session)
@loaded = true
end
end
def stale_session_check!
yield
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
end
retry
else
raise
end
end end
end end

@ -516,7 +516,8 @@ def self.cache_asset_timestamps=(value)
def compute_public_path(source, dir, ext = nil, include_host = true) def compute_public_path(source, dir, ext = nil, include_host = true)
has_request = @controller.respond_to?(:request) has_request = @controller.respond_to?(:request)
if ext && (File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))) source_ext = File.extname(source)[1..-1]
if ext && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))))
source += ".#{ext}" source += ".#{ext}"
end end

@ -30,7 +30,7 @@ module AtomFeedHelper
# app/views/posts/index.atom.builder: # app/views/posts/index.atom.builder:
# atom_feed do |feed| # atom_feed do |feed|
# feed.title("My great blog!") # feed.title("My great blog!")
# feed.updated((@posts.first.created_at)) # feed.updated(@posts.first.created_at)
# #
# for post in @posts # for post in @posts
# feed.entry(post) do |entry| # feed.entry(post) do |entry|

@ -971,7 +971,8 @@ def fields_for_with_nested_attributes(association_name, args, block)
@template.fields_for(child_name, child, *args, &block) @template.fields_for(child_name, child, *args, &block)
end.join end.join
else else
@template.fields_for(name, association, *args, &block) object = args.first.respond_to?(:new_record?) ? args.first : association
@template.fields_for(name, object, *args, &block)
end end
end end

@ -507,7 +507,30 @@ def mail_to(email_address, name = nil, html_options = {})
# current_page?(:controller => 'shop', :action => 'checkout') # current_page?(:controller => 'shop', :action => 'checkout')
# # => true # # => true
# #
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc) # current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc')
# # => false
#
# current_page?(:action => 'checkout')
# # => true
#
# current_page?(:controller => 'library', :action => 'checkout')
# # => false
#
# Let's say we're in the <tt>/shop/checkout?order=desc&page=1</tt> action.
#
# current_page?(:action => 'process')
# # => false
#
# current_page?(:controller => 'shop', :action => 'checkout')
# # => true
#
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page=>'1')
# # => true
#
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page=>'2')
# # => false
#
# current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc')
# # => false # # => false
# #
# current_page?(:action => 'checkout') # current_page?(:action => 'checkout')
@ -516,7 +539,7 @@ def mail_to(email_address, name = nil, html_options = {})
# current_page?(:controller => 'library', :action => 'checkout') # current_page?(:controller => 'library', :action => 'checkout')
# # => false # # => false
def current_page?(options) def current_page?(options)
url_string = CGI.escapeHTML(url_for(options)) url_string = CGI.unescapeHTML(url_for(options))
request = @controller.request request = @controller.request
# We ignore any extra parameters in the request_uri if the # We ignore any extra parameters in the request_uri if the
# submitted url doesn't have any either. This lets the function # submitted url doesn't have any either. This lets the function

@ -2,7 +2,11 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc: class PathSet < Array #:nodoc:
def self.type_cast(obj) def self.type_cast(obj)
if obj.is_a?(String) if obj.is_a?(String)
Template::EagerPath.new(obj) if !Object.const_defined?(:Rails) || Rails.configuration.cache_classes
Template::EagerPath.new(obj)
else
Template::Path.new(obj)
end
else else
obj obj
end end

@ -277,6 +277,9 @@ def render_explicit_html_template
def render_implicit_html_template_from_xhr_request def render_implicit_html_template_from_xhr_request
end end
def render_implicit_js_template_without_layout
end
def formatted_html_erb def formatted_html_erb
end end
@ -681,7 +684,8 @@ def determine_layout
"render_with_explicit_string_template", "render_with_explicit_string_template",
"render_js_with_explicit_template", "render_js_with_explicit_template",
"render_js_with_explicit_action_template", "render_js_with_explicit_action_template",
"delete_with_js", "update_page", "update_page_with_instance_variables" "delete_with_js", "update_page", "update_page_with_instance_variables",
"render_implicit_js_template_without_layout"
"layouts/standard" "layouts/standard"
when "action_talk_to_layout", "layout_overriding_layout" when "action_talk_to_layout", "layout_overriding_layout"
@ -1018,6 +1022,11 @@ def test_should_implicitly_render_html_template_from_xhr_request
assert_equal "Hello HTML!", @response.body assert_equal "Hello HTML!", @response.body
end end
def test_should_implicitly_render_js_template_without_layout
get :render_implicit_js_template_without_layout, :format => :js
assert_no_match /<html>/, @response.body
end
def test_should_render_formatted_template def test_should_render_formatted_template
get :formatted_html_erb get :formatted_html_erb
assert_equal 'formatted html erb', @response.body assert_equal 'formatted html erb', @response.body

@ -0,0 +1 @@
alert('hello');

@ -586,6 +586,15 @@ def test_nested_fields_for_with_a_new_record_on_a_nested_attributes_one_to_one_a
assert_dom_equal expected, output_buffer assert_dom_equal expected, output_buffer
end end
def test_nested_fields_for_with_explicitly_passed_object_on_a_nested_attributes_one_to_one_association
form_for(:post, @post) do |f|
f.fields_for(:author, Author.new(123)) do |af|
assert_not_nil af.object
assert_equal 123, af.object.id
end
end
end
def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association
@post.author = Author.new(321) @post.author = Author.new(321)

@ -252,6 +252,27 @@ def test_link_to_if
assert_equal "Showing", link_to_if(false, "Showing", :action => "show", :controller => "weblog", :id => 1) assert_equal "Showing", link_to_if(false, "Showing", :action => "show", :controller => "weblog", :id => 1)
end end
def test_current_page_with_simple_url
@controller.request = RequestMock.new("http://www.example.com/weblog/show")
@controller.url = "http://www.example.com/weblog/show"
assert current_page?({ :action => "show", :controller => "weblog" })
assert current_page?("http://www.example.com/weblog/show")
end
def test_current_page_ignoring_params
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
@controller.url = "http://www.example.com/weblog/show?order=desc&page=1"
assert current_page?({ :action => "show", :controller => "weblog" })
assert current_page?("http://www.example.com/weblog/show")
end
def test_current_page_with_params_that_match
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
@controller.url = "http://www.example.com/weblog/show?order=desc&page=1"
assert current_page?({ :action => "show", :controller => "weblog", :order => "desc", :page => "1" })
assert current_page?("http://www.example.com/weblog/show?order=desc&amp;page=1")
end
def test_link_unless_current def test_link_unless_current
@controller.request = RequestMock.new("http://www.example.com/weblog/show") @controller.request = RequestMock.new("http://www.example.com/weblog/show")
@controller.url = "http://www.example.com/weblog/show" @controller.url = "http://www.example.com/weblog/show"
@ -263,11 +284,23 @@ def test_link_unless_current
assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" }) assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show") assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show")
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
@controller.url = "http://www.example.com/weblog/show?order=desc&page=1"
assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog", :order=>'desc', :page=>'1' })
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&amp;page=1")
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=1")
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc") @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
@controller.url = "http://www.example.com/weblog/show?order=asc" @controller.url = "http://www.example.com/weblog/show?order=asc"
assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" }) assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=asc") assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=asc")
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
@controller.url = "http://www.example.com/weblog/show?order=desc&page=2"
assert_equal "<a href=\"http://www.example.com/weblog/show?order=desc&amp;page=2\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "<a href=\"http://www.example.com/weblog/show?order=desc&amp;page=2\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=2")
@controller.request = RequestMock.new("http://www.example.com/weblog/show") @controller.request = RequestMock.new("http://www.example.com/weblog/show")
@controller.url = "http://www.example.com/weblog/list" @controller.url = "http://www.example.com/weblog/list"
assert_equal "<a href=\"http://www.example.com/weblog/list\">Listing</a>", assert_equal "<a href=\"http://www.example.com/weblog/list\">Listing</a>",
@ -319,7 +352,7 @@ def test_mail_to_with_replace_options
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
end end
def protect_against_forgery? def protect_against_forgery?
false false
end end

@ -1,3 +1,8 @@
*Edge*
* Added that ActiveRecord::Base.exists? can be called with no arguments #1817 [Scott Taylor]
*2.3.0 [RC1] (February 1st, 2009)* *2.3.0 [RC1] (February 1st, 2009)*
* Add Support for updating deeply nested models from a single form. #1202 [Eloy Duran] * Add Support for updating deeply nested models from a single form. #1202 [Eloy Duran]

@ -1090,6 +1090,22 @@ def belongs_to(association_id, options = {})
# but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
# custom <tt>:join_table</tt> option if you need to. # custom <tt>:join_table</tt> option if you need to.
# #
# The join table should not have a primary key or a model associated with it. You must manually generate the
# join table with a migration such as this:
#
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
# def self.up
# create_table :developers_projects, :id => false do |t|
# t.integer :developer_id
# t.integer :project_id
# end
# end
#
# def self.down
# drop_table :developers_projects
# end
# end
#
# Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through # Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
# +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as # +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
# readonly (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any # readonly (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any

@ -324,6 +324,7 @@ def query_attribute(attr_name)
if Numeric === value || value !~ /[^0-9]/ if Numeric === value || value !~ /[^0-9]/
!value.to_i.zero? !value.to_i.zero?
else else
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
!value.blank? !value.blank?
end end
elsif column.number? elsif column.number?

@ -129,6 +129,7 @@ def self.included(base)
base.class_eval do base.class_eval do
alias_method_chain :reload, :autosave_associations alias_method_chain :reload, :autosave_associations
alias_method_chain :save, :autosave_associations alias_method_chain :save, :autosave_associations
alias_method_chain :save!, :autosave_associations
alias_method_chain :valid?, :autosave_associations alias_method_chain :valid?, :autosave_associations
%w{ has_one belongs_to has_many has_and_belongs_to_many }.each do |type| %w{ has_one belongs_to has_many has_and_belongs_to_many }.each do |type|
@ -161,6 +162,17 @@ def save_with_autosave_associations(perform_validation = true)
end end
end end
# Attempts to save the record just like save_with_autosave_associations but
# will raise a RecordInvalid exception instead of returning false if the
# record is not valid.
def save_with_autosave_associations!
if valid_with_autosave_associations?
save_with_autosave_associations(false) || raise(RecordNotSaved)
else
raise RecordInvalid.new(self)
end
end
# Returns whether or not the parent, <tt>self</tt>, and any loaded autosave associations are valid. # Returns whether or not the parent, <tt>self</tt>, and any loaded autosave associations are valid.
def valid_with_autosave_associations? def valid_with_autosave_associations?
if valid_without_autosave_associations? if valid_without_autosave_associations?

@ -663,7 +663,7 @@ def find_by_sql(sql)
# Returns true if a record exists in the table that matches the +id+ or # Returns true if a record exists in the table that matches the +id+ or
# conditions given, or false otherwise. The argument can take four forms: # conditions given, or false otherwise. The argument can take five forms:
# #
# * Integer - Finds the record with this primary key. # * Integer - Finds the record with this primary key.
# * String - Finds the record with a primary key corresponding to this # * String - Finds the record with a primary key corresponding to this
@ -672,6 +672,7 @@ def find_by_sql(sql)
# (such as <tt>['color = ?', 'red']</tt>). # (such as <tt>['color = ?', 'red']</tt>).
# * Hash - Finds the record that matches these +find+-style conditions # * Hash - Finds the record that matches these +find+-style conditions
# (such as <tt>{:color => 'red'}</tt>). # (such as <tt>{:color => 'red'}</tt>).
# * No args - Returns false if the table is empty, true otherwise.
# #
# For more information about specifying conditions as a Hash or Array, # For more information about specifying conditions as a Hash or Array,
# see the Conditions section in the introduction to ActiveRecord::Base. # see the Conditions section in the introduction to ActiveRecord::Base.
@ -685,7 +686,8 @@ def find_by_sql(sql)
# Person.exists?('5') # Person.exists?('5')
# Person.exists?(:name => "David") # Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists?(['name LIKE ?', "%#{query}%"])
def exists?(id_or_conditions) # Person.exists?
def exists?(id_or_conditions = {})
connection.select_all( connection.select_all(
construct_finder_sql( construct_finder_sql(
:select => "#{quoted_table_name}.#{primary_key}", :select => "#{quoted_table_name}.#{primary_key}",
@ -1990,12 +1992,16 @@ def all_attributes_exists?(attribute_names)
attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) } attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
end end
def attribute_condition(argument) def attribute_condition(quoted_column_name, argument)
case argument case argument
when nil then "IS ?" when nil then "#{quoted_column_name} IS ?"
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)" when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "#{quoted_column_name} IN (?)"
when Range then "BETWEEN ? AND ?" when Range then if argument.exclude_end?
else "= ?" "#{quoted_column_name} >= ? AND #{quoted_column_name} < ?"
else
"#{quoted_column_name} BETWEEN ? AND ?"
end
else "#{quoted_column_name} = ?"
end end
end end
@ -2305,7 +2311,7 @@ def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
table_name = connection.quote_table_name(table_name) table_name = connection.quote_table_name(table_name)
end end
"#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value)
else else
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
end end

@ -8,6 +8,7 @@ module ConnectionAdapters #:nodoc:
# An abstract definition of a column in a table. # An abstract definition of a column in a table.
class Column class Column
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
module Format module Format
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/ ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/

@ -86,7 +86,8 @@ def self.included(base)
# For each key in the hash that starts with the string 'new' a new model # For each key in the hash that starts with the string 'new' a new model
# will be instantiated. When the proc given with the <tt>:reject_if</tt> # will be instantiated. When the proc given with the <tt>:reject_if</tt>
# option evaluates to +false+ for a certain attribute hash no record will # option evaluates to +false+ for a certain attribute hash no record will
# be built for that hash. # be built for that hash. (Rejecting new records can alternatively be done
# by utilizing the <tt>'_delete'</tt> key. Scroll down for more info.)
# #
# params = { 'member' => { # params = { 'member' => {
# 'name' => 'joe', 'posts_attributes' => { # 'name' => 'joe', 'posts_attributes' => {
@ -258,11 +259,14 @@ def should_destroy_nested_attributes_record?(allow_destroy, attributes)
# If a <tt>:reject_if</tt> proc exists for this association, it will be # If a <tt>:reject_if</tt> proc exists for this association, it will be
# called with the attributes as its argument. If the proc returns a truthy # called with the attributes as its argument. If the proc returns a truthy
# value, the record is _not_ build. # value, the record is _not_ build.
#
# Alternatively, you can specify the <tt>'_delete'</tt> key to _not_ build
# a record. See should_destroy_nested_attributes_record? for more info.
def build_new_nested_attributes_record(association_name, attributes) def build_new_nested_attributes_record(association_name, attributes)
if reject_proc = self.class.reject_new_nested_attributes_procs[association_name] if reject_proc = self.class.reject_new_nested_attributes_procs[association_name]
return if reject_proc.call(attributes) return if reject_proc.call(attributes)
end end
send(association_name).build(attributes) send(association_name).build(attributes) unless should_destroy_nested_attributes_record?(true, attributes)
end end
# Assigns the attributes to the record specified by +id+. Or marks it for # Assigns the attributes to the record specified by +id+. Or marks it for

@ -99,7 +99,7 @@ def self.find_by_session_id(*args)
define_method(:session_id=) { |session_id| self.sessid = session_id } define_method(:session_id=) { |session_id| self.sessid = session_id }
else else
def self.find_by_session_id(session_id) def self.find_by_session_id(session_id)
find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id] find :first, :conditions => {:session_id=>session_id}
end end
end end
end end

@ -744,7 +744,7 @@ def validates_uniqueness_of(*attr_names)
if scope = configuration[:scope] if scope = configuration[:scope]
Array(scope).map do |scope_item| Array(scope).map do |scope_item|
scope_value = record.send(scope_item) scope_value = record.send(scope_item)
condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}" condition_sql << " AND " << attribute_condition("#{record.class.quoted_table_name}.#{scope_item}", scope_value)
condition_params << scope_value condition_params << scope_value
end end
end end

@ -56,6 +56,18 @@ def test_should_unserialize_attributes_for_frozen_records
assert_equal myobj, topic.content assert_equal myobj, topic.content
end end
def test_typecast_attribute_from_select_to_false
topic = Topic.create(:title => 'Budget')
topic = Topic.find(:first, :select => "topics.*, 1=2 as is_test")
assert !topic.is_test?
end
def test_typecast_attribute_from_select_to_true
topic = Topic.create(:title => 'Budget')
topic = Topic.find(:first, :select => "topics.*, 2=2 as is_test")
assert topic.is_test?
end
def test_kernel_methods_not_implemented_in_activerecord def test_kernel_methods_not_implemented_in_activerecord
%w(test name display y).each do |method| %w(test name display y).each do |method|
assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined" assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"

@ -169,6 +169,12 @@ def test_should_automatically_save_the_associated_model
assert_equal 'The Vile Insanity', @pirate.reload.ship.name assert_equal 'The Vile Insanity', @pirate.reload.ship.name
end end
def test_should_automatically_save_bang_the_associated_model
@pirate.ship.name = 'The Vile Insanity'
@pirate.save!
assert_equal 'The Vile Insanity', @pirate.reload.ship.name
end
def test_should_automatically_validate_the_associated_model def test_should_automatically_validate_the_associated_model
@pirate.ship.name = '' @pirate.ship.name = ''
assert !@pirate.valid? assert !@pirate.valid?
@ -245,6 +251,12 @@ def test_should_automatically_save_the_associated_model
assert_equal 'Arr', @ship.reload.pirate.catchphrase assert_equal 'Arr', @ship.reload.pirate.catchphrase
end end
def test_should_automatically_save_bang_the_associated_model
@ship.pirate.catchphrase = 'Arr'
@ship.save!
assert_equal 'Arr', @ship.reload.pirate.catchphrase
end
def test_should_automatically_validate_the_associated_model def test_should_automatically_validate_the_associated_model
@ship.pirate.catchphrase = '' @ship.pirate.catchphrase = ''
assert !@ship.valid? assert !@ship.valid?
@ -298,6 +310,14 @@ def test_should_automatically_save_the_associated_models
assert_equal new_names, @pirate.reload.send(@association_name).map(&:name) assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
end end
def test_should_automatically_save_bang_the_associated_models
new_names = ['Grace OMalley', 'Privateers Greed']
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
@pirate.save!
assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
end
def test_should_automatically_validate_the_associated_models def test_should_automatically_validate_the_associated_models
@pirate.send(@association_name).each { |child| child.name = '' } @pirate.send(@association_name).each { |child| child.name = '' }
@ -347,7 +367,9 @@ def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_t
def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! } assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
assert_queries(2) do @pirate.send(@association_name).class # hack to load the target
assert_queries(3) do
@pirate.catchphrase = 'Yarr' @pirate.catchphrase = 'Yarr'
new_names = ['Grace OMalley', 'Privateers Greed'] new_names = ['Grace OMalley', 'Privateers Greed']
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] } @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }

@ -26,6 +26,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
def setup def setup
@connection = ActiveRecord::Base.connection @connection = ActiveRecord::Base.connection
@connection.execute("set lc_monetary = 'C'")
@connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )") @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )")
@first_array = PostgresqlArray.find(1) @first_array = PostgresqlArray.find(1)

@ -94,7 +94,16 @@ def test_exists
assert_raise(NoMethodError) { Topic.exists?([1,2]) } assert_raise(NoMethodError) { Topic.exists?([1,2]) }
end end
def test_exists_returns_true_with_one_record_and_no_args
assert Topic.exists?
end
def test_does_not_exist_with_empty_table_and_no_args_given
Topic.delete_all
assert !Topic.exists?
end
def test_exists_with_aggregate_having_three_mappings def test_exists_with_aggregate_having_three_mappings
existing_address = customers(:david).address existing_address = customers(:david).address
assert Customer.exists?(:address => existing_address) assert Customer.exists?(:address => existing_address)
@ -298,6 +307,12 @@ def test_find_on_hash_conditions_with_range
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) } assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
end end
def test_find_on_hash_conditions_with_end_exclusive_range
assert_equal [1,2,3], Topic.find(:all, :conditions => { :id => 1..3 }).map(&:id).sort
assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1...3 }).map(&:id).sort
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(3, :conditions => { :id => 2...3 }) }
end
def test_find_on_hash_conditions_with_multiple_ranges def test_find_on_hash_conditions_with_multiple_ranges
assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort
assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort

@ -233,6 +233,20 @@ def test_should_automatically_build_new_associated_models_for_each_entry_in_a_ha
assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
end end
def test_should_remove_delete_key_from_arguments_hash_of_new_records
assert_nothing_raised ActiveRecord::UnknownAttributeError do
@pirate.send(association_setter, { 'new_1' => { '_delete' => '0' }})
end
end
def test_should_ignore_new_associated_records_with_truthy_delete_attribute
@pirate.send(@association_name).destroy_all
@pirate.reload.attributes = { association_getter => { 'new_1' => { :name => 'Grace OMalley' }, 'new_2' => { :name => 'Privateers Greed', '_delete' => '1' }}}
assert_equal 1, @pirate.send(@association_name).length
assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
end
def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
attributes = ActiveSupport::OrderedHash.new attributes = ActiveSupport::OrderedHash.new
attributes['new_123726353'] = { :name => 'Grace OMalley' } attributes['new_123726353'] = { :name => 'Grace OMalley' }

@ -284,7 +284,7 @@ def transliterate(string)
# The iconv transliteration code doesn't function correctly # The iconv transliteration code doesn't function correctly
# on some platforms, but it's very fast where it does function. # on some platforms, but it's very fast where it does function.
elsif "foo" != Inflector.transliterate("föö") elsif "foo" != (Inflector.transliterate("föö") rescue nil)
undef_method :transliterate undef_method :transliterate
def transliterate(string) def transliterate(string)
string.mb_chars.normalize(:kd). # Decompose accented characters string.mb_chars.normalize(:kd). # Decompose accented characters

@ -244,6 +244,11 @@ def copy_with_rewritten_ruby_path(src_file, dest_file)
end end
end end
desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"'
task :guides do
ruby "guides/rails_guides.rb"
end
# Generate documentation ------------------------------------------------------------------ # Generate documentation ------------------------------------------------------------------

@ -0,0 +1,188 @@
/* Unobtrustive Code Highlighter By Dan Webb 11/2005
Version: 0.4
Usage:
Add a script tag for this script and any stylesets you need to use
to the page in question, add correct class names to CODE elements,
define CSS styles for elements. That's it!
Known to work on:
IE 5.5+ PC
Firefox/Mozilla PC/Mac
Opera 7.23 + PC
Safari 2
Known to degrade gracefully on:
IE5.0 PC
Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors
in older browsers use expressions that use lookahead in string format when defining stylesets.
This script is inspired by star-light by entirely cunning Dean Edwards
http://dean.edwards.name/star-light/.
*/
// replace callback support for safari.
if ("a".replace(/a/, function() {return "b"}) != "b") (function(){
var default_replace = String.prototype.replace;
String.prototype.replace = function(search,replace){
// replace is not function
if(typeof replace != "function"){
return default_replace.apply(this,arguments)
}
var str = "" + this;
var callback = replace;
// search string is not RegExp
if(!(search instanceof RegExp)){
var idx = str.indexOf(search);
return (
idx == -1 ? str :
default_replace.apply(str,[search,callback(search, idx, str)])
)
}
var reg = search;
var result = [];
var lastidx = reg.lastIndex;
var re;
while((re = reg.exec(str)) != null){
var idx = re.index;
var args = re.concat(idx, str);
result.push(
str.slice(lastidx,idx),
callback.apply(null,args).toString()
);
if(!reg.global){
lastidx += RegExp.lastMatch.length;
break
}else{
lastidx = reg.lastIndex;
}
}
result.push(str.slice(lastidx));
return result.join("")
}
})();
var CodeHighlighter = { styleSets : new Array };
CodeHighlighter.addStyle = function(name, rules) {
// using push test to disallow older browsers from adding styleSets
if ([].push) this.styleSets.push({
name : name,
rules : rules,
ignoreCase : arguments[2] || false
})
function setEvent() {
// set highlighter to run on load (use LowPro if present)
if (typeof Event != 'undefined' && typeof Event.onReady == 'function')
return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter));
var old = window.onload;
if (typeof window.onload != 'function') {
window.onload = function() { CodeHighlighter.init() };
} else {
window.onload = function() {
old();
CodeHighlighter.init();
}
}
}
// only set the event when the first style is added
if (this.styleSets.length==1) setEvent();
}
CodeHighlighter.init = function() {
if (!document.getElementsByTagName) return;
if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function
// throw out older browsers
var codeEls = document.getElementsByTagName("CODE");
// collect array of all pre elements
codeEls.filter = function(f) {
var a = new Array;
for (var i = 0; i < this.length; i++) if (f(this[i])) a[a.length] = this[i];
return a;
}
var rules = new Array;
rules.toString = function() {
// joins regexes into one big parallel regex
var exps = new Array;
for (var i = 0; i < this.length; i++) exps.push(this[i].exp);
return exps.join("|");
}
function addRule(className, rule) {
// add a replace rule
var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp;
// converts regex rules to strings and chops of the slashes
rules.push({
className : className,
exp : "(" + exp + ")",
length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule
replacement : rule.replacement || null
});
}
function parse(text, ignoreCase) {
// main text parsing and replacement
return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() {
var i = 0, j = 1, rule;
while (rule = rules[i++]) {
if (arguments[j]) {
// if no custom replacement defined do the simple replacement
if (!rule.replacement) return "<span class=\"" + rule.className + "\">" + arguments[0] + "</span>";
else {
// replace $0 with the className then do normal replaces
var str = rule.replacement.replace("$0", rule.className);
for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]);
return str;
}
} else j+= rule.length;
}
});
}
function highlightCode(styleSet) {
// clear rules array
var parsed, clsRx = new RegExp("(\\s|^)" + styleSet.name + "(\\s|$)");
rules.length = 0;
// get stylable elements by filtering out all code elements without the correct className
var stylableEls = codeEls.filter(function(item) { return clsRx.test(item.className) });
// add style rules to parser
for (var className in styleSet.rules) addRule(className, styleSet.rules[className]);
// replace for all elements
for (var i = 0; i < stylableEls.length; i++) {
// EVIL hack to fix IE whitespace badness if it's inside a <pre>
if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
stylableEls[i] = stylableEls[i].parentNode;
parsed = stylableEls[i].innerHTML.replace(/(<code[^>]*>)([^<]*)<\/code>/i, function() {
return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + "</code>"
});
parsed = parsed.replace(/\n( *)/g, function() {
var spaces = "";
for (var i = 0; i < arguments[1].length; i++) spaces+= "&nbsp;";
return "\n" + spaces;
});
parsed = parsed.replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;");
parsed = parsed.replace(/\n(<\/\w+>)?/g, "<br />$1").replace(/<br \/>[\n\r\s]*<br \/>/g, "<p><br></p>");
} else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase);
stylableEls[i].innerHTML = parsed;
}
}
// run highlighter on all stylesets
for (var i=0; i < this.styleSets.length; i++) {
highlightCode(this.styleSets[i]);
}
}

@ -0,0 +1,8 @@
function guideMenu(){
if (document.getElementById('guides').style.display == "none") {
document.getElementById('guides').style.display = "block";
} else {
document.getElementById('guides').style.display = "none";
}
}

@ -0,0 +1,90 @@
CodeHighlighter.addStyle("css", {
comment : {
exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\//
},
keywords : {
exp : /@\w[\w\s]*/
},
selectors : {
exp : "([\\w-:\\[.#][^{};>]*)(?={)"
},
properties : {
exp : "([\\w-]+)(?=\\s*:)"
},
units : {
exp : /([0-9])(em|en|px|%|pt)\b/,
replacement : "$1<span class=\"$0\">$2</span>"
},
urls : {
exp : /url\([^\)]*\)/
}
});
CodeHighlighter.addStyle("ruby",{
comment : {
exp : /#[^\n]+/
},
brackets : {
exp : /\(|\)/
},
string : {
exp : /'[^']*'|"[^"]*"/
},
keywords : {
exp : /\b(do|end|self|class|def|if|module|yield|then|else|for|until|unless|while|elsif|case|when|break|retry|redo|rescue|require|raise)\b/
},
/* Added by Shelly Fisher (shelly@agileevolved.com) */
symbol : {
exp : /([^:])(:[A-Za-z0-9_!?]+)/
},
ivar : {
exp : /\@[A-Za-z0-9_!?]+/
}
});
CodeHighlighter.addStyle("html", {
comment : {
exp: /&lt;!\s*(--([^-]|[\r\n]|-[^-])*--\s*)&gt;/
},
tag : {
exp: /(&lt;\/?)([a-zA-Z1-9]+\s?)/,
replacement: "$1<span class=\"$0\">$2</span>"
},
string : {
exp : /'[^']*'|"[^"]*"/
},
attribute : {
exp: /\b([a-zA-Z-:]+)(=)/,
replacement: "<span class=\"$0\">$1</span>$2"
},
doctype : {
exp: /&lt;!DOCTYPE([^&]|&[^g]|&g[^t])*&gt;/
}
});
CodeHighlighter.addStyle("javascript",{
comment : {
exp : /(\/\/[^\n]*(\n|$))|(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)/
},
brackets : {
exp : /\(|\)/
},
string : {
exp : /'[^']*'|"[^"]*"/
},
keywords : {
exp : /\b(arguments|break|case|continue|default|delete|do|else|false|for|function|if|in|instanceof|new|null|return|switch|this|true|typeof|var|void|while|with)\b/
},
global : {
exp : /\b(toString|valueOf|window|element|prototype|constructor|document|escape|unescape|parseInt|parseFloat|setTimeout|clearTimeout|setInterval|clearInterval|NaN|isNaN|Infinity)\b/
}
});
CodeHighlighter.addStyle("yaml", {
keyword : {
exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\//
},
value : {
exp : /@\w[\w\s]*/
},
});

@ -0,0 +1,436 @@
/* Guides.rubyonrails.org */
/* Main.css */
/* Created January 30, 2009 */
/* Modified January 31, 2009
--------------------------------------- */
/* General
--------------------------------------- */
.left {float: left; margin-right: 1em;}
.right {float: right; margin-left: 1em;}
.small {font-size: smaller;}
.large {font-size: larger;}
.hide {display: none;}
li ul, li ol { margin:0 1.5em; }
ul, ol { margin: 0 1.5em 1.5em 1.5em; }
ul { list-style-type: disc; }
ol { list-style-type: decimal; }
dl { margin: 0 0 1.5em 0; }
dl dt { font-weight: bold; }
dd { margin-left: 1.5em;}
pre,code { margin: 1.5em 0; white-space: pre; }
pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; }
abbr, acronym { border-bottom: 1px dotted #666; }
address { margin: 0 0 1.5em; font-style: italic; }
del { color:#666; }
blockquote { margin: 1.5em; color: #666; font-style: italic; }
strong { font-weight: bold; }
em, dfn { font-style: italic; }
dfn { font-weight: bold; }
sup, sub { line-height: 0; }
p {margin: 0 0 1.5em;}
label { font-weight: bold; }
fieldset { padding:1.4em; margin: 0 0 1.5em 0; border: 1px solid #ccc; }
legend { font-weight: bold; font-size:1.2em; }
input.text, input.title,
textarea, select {
margin:0.5em 0;
border:1px solid #bbb;
}
table {
margin: 1em 0;
border: 1px solid #ddd;
background: #f4f4f4;
border-spacing: 0;
}
table th, table td {
padding: 0.25em;
border-right: 1px dotted #e0e0e0;
border-bottom: 1px dotted #e0e0e0;
}
table th:last-child, table td:last-child {
border-right: none;
}
table th {
border-bottom: 1px solid #ddd;
background: #f0f0f0;
font-weight: bold;
}
table td {
}
table tt {
padding: 0.1em;
}
/* Structure and Layout
--------------------------------------- */
body {
text-align: center;
font-family: Helvetica, Arial, sans-serif;
font-size: 87.5%;
line-height: 1.5em;
background: #222;
color: #999;
}
.wrapper {
text-align: left;
margin: 0 auto;
width: 69em;
}
#topNav {
padding: 1em 0;
color: #565656;
}
#header {
background: #c52f24 url(../../images/header_tile.gif) repeat-x;
color: #FFF;
padding: 1.5em 0;
position: relative;
z-index: 99;
}
#feature {
background: #d5e9f6 url(../../images/feature_tile.gif) repeat-x;
color: #333;
padding: 0.5em 0 1.5em;
}
#container {
background: #FFF;
color: #333;
padding: 0.5em 0 1.5em 0;
}
#mainCol {
width: 45em;
margin-left: 2em;
}
#subCol {
position: absolute;
z-index: 0;
top: 0;
right: 0;
background: #FFF;
padding: 1em 1.5em 1em 1.25em;
width: 17em;
font-size: 0.9285em;
line-height: 1.3846em;
}
#extraCol {display: none;}
#footer {
padding: 2em 0;
background: url(../../images/footer_tile.gif) repeat-x;
}
#footer .wrapper {
padding-left: 2em;
width: 67em;
}
#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; width: 68em;}
#feature .wrapper {width: 45em; padding-right: 23em; position: relative; z-index: 0;}
/* Links
--------------------------------------- */
a, a:link, a:visited {
color: #ee3f3f;
text-decoration: underline;
}
#mainCol a, #subCol a {color: #980905;}
/* Navigation
--------------------------------------- */
.nav {margin: 0; padding: 0;}
.nav li {display: inline; list-style: none;}
#header .nav {
float: right;
margin-top: 1.5em;
font-size: 1.2857em;
}
#header .nav li {margin: 0 0 0 0.5em;}
#header .nav a {color: #FFF; text-decoration: none;}
#header .nav a:hover {text-decoration: underline;}
#header .nav .index {
padding: 0.5em 1.5em;
border-radius: 1em;
-webkit-border-radius: 1em;
-moz-border-radius: 1em;
background: #980905;
position: relative;
}
#header .nav .index a {
background: #980905 url(../../images/nav_arrow.gif) no-repeat right top;
padding-right: 1em;
position: relative;
z-index: 15;
padding-bottom: 0.125em;
}
#header .nav .index:hover a, #header .nav .index a:hover {background-position: right -81px;}
#guides {
width: 27em;
display: block;
background: #980905;
border-radius: 1em;
-webkit-border-radius: 1em;
-moz-border-radius: 1em;
-webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
-moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
color: #f1938c;
padding: 1.5em 2em;
position: absolute;
z-index: 10;
top: -0.25em;
right: 0;
padding-top: 2em;
}
#guides dt, #guides dd {
font-weight: normal;
font-size: 0.722em;
margin: 0;
padding: 0;
}
#guides dt {padding:0; margin: 0.5em 0 0;}
#guides a {color: #FFF; background: none !important;}
#guides .L, #guides .R {float: left; width: 50%; margin: 0; padding: 0;}
#guides .R {float: right;}
#guides hr {
display: block;
border: none;
height: 1px;
color: #f1938c;
background: #f1938c;
}
/* Headings
--------------------------------------- */
h1 {
font-size: 2.5em;
line-height: 1em;
margin: 0.6em 0 .2em;
font-weight: bold;
}
h2 {
font-size: 2.1428em;
line-height: 1em;
margin: 0.7em 0 .2333em;
font-weight: bold;
}
h3 {
font-size: 1.7142em;
line-height: 1.286em;
margin: 0.875em 0 0.2916em;
font-weight: bold;
}
h4 {
font-size: 1.2857em;
line-height: 1.2em;
margin: 1.6667em 0 .3887em;
font-weight: bold;
}
h5 {
font-size: 1em;
line-height: 1.5em;
margin: 1em 0 .5em;
font-weight: bold;
}
h6 {
font-size: 1em;
line-height: 1.5em;
margin: 1em 0 .5em;
font-weight: normal;
}
/* Content
--------------------------------------- */
.pic {
margin: 0 2em 2em 0;
}
#topNav strong {color: #999; margin-right: 0.5em;}
#topNav strong a {color: #FFF;}
#header h1 {
float: left;
background: url(../../images/ruby_guides_logo.gif) no-repeat;
width: 492px;
text-indent: -9999em;
margin: 0;
padding: 0;
}
#header h1 a {
text-decoration: none;
display: block;
height: 77px;
}
#feature p {
font-size: 1.2857em;
margin-bottom: 0.75em;
}
#feature ul {margin-left: 0;}
#feature ul li {
list-style: none;
background: url(../../images/check_bullet.gif) no-repeat left 0.5em;
padding: 0.5em 1.75em 0.5em 1.75em;
font-size: 1.1428em;
font-weight: bold;
}
#mainCol dd, #subCol dd {
padding: 0.25em 0 1em;
border-bottom: 1px solid #CCC;
margin-bottom: 1em;
margin-left: 0;
padding-left: 28px;
}
#mainCol dt, #subCol dt {
font-size: 1.2857em;
padding: 0.125em 0 0.25em 28px;
margin-bottom: 0;
background: url(../../images/book_icon.gif) no-repeat left top;
}
#mainCol dd.ticket, #subCol dd.ticket {
background: #fff9d8 url(../../images/tab_yellow.gif) no-repeat left top;
border: none;
padding: 1.25em 1em 1.25em 48px;
margin-left: 0;
margin-top: 0.25em;
}
#mainCol dd.warning, #subCol dd.warning {
background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
border: none;
padding: 1.25em 1.25em 1.25em 48px;
margin-left: 0;
margin-top: 0.25em;
}
#subCol .chapters {color: #980905;}
#subCol .chapters a {font-weight: bold;}
#subCol .chapters ul a {font-weight: normal;}
#subCol .chapters li {margin-bottom: 0.75em;}
#subCol h3.chapter {margin-top: 0.25em;}
#subCol h3.chapter img {vertical-align: text-bottom;}
#subCol .chapters ul {margin-left: 0; margin-top: 0.5em;}
#subCol .chapters ul li {
list-style: none;
padding: 0 0 0 1em;
background: url(../../images/bullet.gif) no-repeat left 0.45em;
margin-left: 0;
font-size: 1em;
font-weight: normal;
}
tt {
background: #EEE;
border: 1px solid #CCC;
padding: 0.25em 0.5em;
font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
}
code, pre {
font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
background: #EEE url(../../images/tab_grey.gif) no-repeat left top;
border: none;
padding: 0.25em 1em 0.5em 48px;
margin-left: 0;
margin-top: 0.25em;
display: block;
}
.note {
background: #fff9d8 url(../../images/tab_note.gif) no-repeat left top;
border: none;
padding: 1em 1em 0.25em 48px;
margin-left: 0;
margin-top: 0.25em;
}
.info {
background: #d5e9f6 url(../../images/tab_info.gif) no-repeat left top;
border: none;
padding: 1em 1em 0.25em 48px;
margin-left: 0;
margin-top: 0.25em;
}
.warning {
background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
border: none;
padding: 1em 1em 0.25em 48px;
margin-left: 0;
margin-top: 0.25em;
}
.warning tt, .note tt, .info tt {border:none; background: none; padding: 0;}
em.highlight {
background: #fffcdb;
padding: 0 0.25em;
}
#mainCol ul li {
list-style:none;
background: url(../../images/grey_bullet.gif) no-repeat left 0.5em;
padding-left: 1em;
margin-left: 0;
}
/* Clearing
--------------------------------------- */
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix {display: inline-block;}
* html .clearfix {height: 1%;}
.clearfix {display: block;}
.clear { clear:both; }

@ -0,0 +1,52 @@
/* Guides.rubyonrails.org */
/* Print.css */
/* Created January 30, 2009 */
/* Modified January 31, 2009
--------------------------------------- */
body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, #extraCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;}
body {
background: #FFF;
font-size: 10pt !important;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.5;
color: #000;
padding: 0 3%;
}
.hide, .nav {
display: none !important;
}
a:link, a:visited {
background: transparent;
font-weight: bold;
text-decoration: underline;
}
hr {
background:#ccc;
color:#ccc;
width:100%;
height:2px;
margin:2em 0;
padding:0;
border:none;
}
h1,h2,h3,h4,h5,h6 { font-family: "Helvetica Neue", Arial, "Lucida Grande", sans-serif; }
code { font:.9em "Courier New", Monaco, Courier, monospace; }
img { float:left; margin:1.5em 1.5em 1.5em 0; }
a img { border:none; }
blockquote {
margin:1.5em;
padding:1em;
font-style:italic;
font-size:.9em;
}
.small { font-size: .9em; }
.large { font-size: 1.1em; }

@ -0,0 +1,43 @@
/* Guides.rubyonrails.org */
/* Reset.css */
/* Created January 30, 2009
--------------------------------------- */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
background: transparent;
}
body {line-height: 1; color: black; background: white;}
a img {border:none;}
ins {text-decoration: none;}
del {text-decoration: line-through;}
:focus {
-moz-outline:0;
outline:0;
outline-offset:0;
}
/* tables still need 'cellspacing="0"' in the markup */
table {border-collapse: collapse; border-spacing: 0;}
caption, th, td {text-align: left; font-weight: normal;}
blockquote, q {quotes: none;}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}

@ -0,0 +1,13 @@
/* Guides.rubyonrails.org */
/* Style.css */
/* Created January 30, 2009
--------------------------------------- */
/*
---------------------------------------
Import advanced style sheet
---------------------------------------
*/
@import url("reset.css");
@import url("main.css");

@ -0,0 +1,31 @@
.html .tag {
color : green;
}
.html .doctype {
color: #708090;
}
.erb .tag {
color : green;
}
.erb .doctype {
color: #708090;
}
.ruby .keywords {
color : red;
}
.ruby .ivar {
color : blue;
}
.ruby .comment {
color: #708090;
}
.ruby .symbol {
color: green;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@ -0,0 +1,5 @@
Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook
icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency
from the Jimmac icons to get round MS IE and FOP PNG incompatibilies.
Stuart Rackham

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Some files were not shown because too many files have changed in this diff Show More