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/doc/guides/html/images
railties/doc/guides/html/stylesheets
railties/guides/output
*.rbc
*.swp
*.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)*
* Fixed RFC-2045 quoted-printable bug #1421 [squadette]

@ -672,7 +672,7 @@ def create_mail
def perform_delivery_smtp(mail)
destinations = mail.destinations
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.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
TestMailer.deliver_return_path
assert_match %r{^Return-Path: <another@somewhere.test>}, MockSMTP.deliveries[0][0]
assert_equal "another@somewhere.test", MockSMTP.deliveries[0][1].to_s
end
def test_body_is_stored_as_an_ivar

@ -1,5 +1,7 @@
*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]

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

@ -173,7 +173,7 @@ def layout_conditions #:nodoc:
end
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)
find_layout(layout, format)
end

@ -58,9 +58,28 @@ def loaded?
end
def load!
@id, session = @by.send(:load_session, @env)
replace(session)
@loaded = true
stale_session_check! do
@id, session = @by.send(:load_session, @env)
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

@ -516,7 +516,8 @@ def self.cache_asset_timestamps=(value)
def compute_public_path(source, dir, ext = nil, include_host = true)
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}"
end

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

@ -507,7 +507,30 @@ def mail_to(email_address, name = nil, html_options = {})
# current_page?(:controller => 'shop', :action => 'checkout')
# # => 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
#
# current_page?(:action => 'checkout')
@ -516,7 +539,7 @@ def mail_to(email_address, name = nil, html_options = {})
# current_page?(:controller => 'library', :action => 'checkout')
# # => false
def current_page?(options)
url_string = CGI.escapeHTML(url_for(options))
url_string = CGI.unescapeHTML(url_for(options))
request = @controller.request
# We ignore any extra parameters in the request_uri if the
# submitted url doesn't have any either. This lets the function

@ -2,7 +2,11 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
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
obj
end

@ -277,6 +277,9 @@ def render_explicit_html_template
def render_implicit_html_template_from_xhr_request
end
def render_implicit_js_template_without_layout
end
def formatted_html_erb
end
@ -681,7 +684,8 @@ def determine_layout
"render_with_explicit_string_template",
"render_js_with_explicit_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"
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
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
get :formatted_html_erb
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
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
@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)
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
@controller.request = RequestMock.new("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", "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.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", "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.url = "http://www.example.com/weblog/list"
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%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
def protect_against_forgery?
false
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)*
* 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
# 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
# +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

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

@ -129,6 +129,7 @@ def self.included(base)
base.class_eval do
alias_method_chain :reload, :autosave_associations
alias_method_chain :save, :autosave_associations
alias_method_chain :save!, :autosave_associations
alias_method_chain :valid?, :autosave_associations
%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
# 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.
def valid_with_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
# 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.
# * 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>).
# * Hash - Finds the record that matches these +find+-style conditions
# (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,
# see the Conditions section in the introduction to ActiveRecord::Base.
@ -685,7 +686,8 @@ def find_by_sql(sql)
# Person.exists?('5')
# Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"])
def exists?(id_or_conditions)
# Person.exists?
def exists?(id_or_conditions = {})
connection.select_all(
construct_finder_sql(
: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) }
end
def attribute_condition(argument)
def attribute_condition(quoted_column_name, argument)
case argument
when nil then "IS ?"
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
when Range then "BETWEEN ? AND ?"
else "= ?"
when nil then "#{quoted_column_name} IS ?"
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "#{quoted_column_name} IN (?)"
when Range then if argument.exclude_end?
"#{quoted_column_name} >= ? AND #{quoted_column_name} < ?"
else
"#{quoted_column_name} BETWEEN ? AND ?"
end
else "#{quoted_column_name} = ?"
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)
end
"#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value)
else
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
end

@ -8,6 +8,7 @@ module ConnectionAdapters #:nodoc:
# An abstract definition of a column in a table.
class Column
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
module Format
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
# 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
# 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' => {
# '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
# called with the attributes as its argument. If the proc returns a truthy
# 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)
if reject_proc = self.class.reject_new_nested_attributes_procs[association_name]
return if reject_proc.call(attributes)
end
send(association_name).build(attributes)
send(association_name).build(attributes) unless should_destroy_nested_attributes_record?(true, attributes)
end
# 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 }
else
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

@ -744,7 +744,7 @@ def validates_uniqueness_of(*attr_names)
if scope = configuration[:scope]
Array(scope).map do |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
end
end

@ -56,6 +56,18 @@ def test_should_unserialize_attributes_for_frozen_records
assert_equal myobj, topic.content
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
%w(test name display y).each do |method|
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
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
@pirate.ship.name = ''
assert !@pirate.valid?
@ -245,6 +251,12 @@ def test_should_automatically_save_the_associated_model
assert_equal 'Arr', @ship.reload.pirate.catchphrase
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
@ship.pirate.catchphrase = ''
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)
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
@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
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'
new_names = ['Grace OMalley', 'Privateers Greed']
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }

@ -26,6 +26,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
def setup
@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}' )")
@first_array = PostgresqlArray.find(1)

@ -94,7 +94,16 @@ def test_exists
assert_raise(NoMethodError) { Topic.exists?([1,2]) }
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
existing_address = customers(:david).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 }) }
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
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

@ -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
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
attributes = ActiveSupport::OrderedHash.new
attributes['new_123726353'] = { :name => 'Grace OMalley' }

@ -284,7 +284,7 @@ def transliterate(string)
# The iconv transliteration code doesn't function correctly
# 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
def transliterate(string)
string.mb_chars.normalize(:kd). # Decompose accented characters

@ -244,6 +244,11 @@ def copy_with_rewritten_ruby_path(src_file, dest_file)
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 ------------------------------------------------------------------

@ -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