Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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

  • Loading branch information...
commit 2139a1b6812be7ca86de2df52e9776a2be4a2bf7 2 parents 09b7e35 + 095cafb
@radar radar authored
Showing with 2,555 additions and 1,053 deletions.
  1. +10 −0 actionpack/CHANGELOG
  2. +3 −1 actionpack/lib/action_controller/base.rb
  3. +1 −3 actionpack/lib/action_controller/routing/builder.rb
  4. +3 −0  actionpack/lib/action_controller/test_case.rb
  5. +1 −1  actionpack/lib/action_view/helpers/asset_tag_helper.rb
  6. +56 −8 actionpack/lib/action_view/helpers/atom_feed_helper.rb
  7. +4 −3 actionpack/lib/action_view/helpers/form_tag_helper.rb
  8. +6 −12 actionpack/lib/action_view/helpers/prototype_helper.rb
  9. +6 −0 actionpack/test/controller/render_test.rb
  10. +21 −0 actionpack/test/controller/routing_test.rb
  11. +1 −0  actionpack/test/controller/test_test.rb
  12. +13 −0 actionpack/test/template/asset_tag_helper_test.rb
  13. +81 −0 actionpack/test/template/atom_feed_helper_test.rb
  14. +2 −1  actionpack/test/template/form_tag_helper_test.rb
  15. +3 −3 actionpack/test/template/prototype_helper_test.rb
  16. +3 −0  activerecord/lib/active_record/association_preload.rb
  17. +8 −13 activerecord/lib/active_record/associations.rb
  18. +11 −1 activerecord/lib/active_record/associations/association_collection.rb
  19. +11 −0 activerecord/lib/active_record/associations/association_proxy.rb
  20. +1 −0  activerecord/lib/active_record/associations/has_many_association.rb
  21. +1 −1  activerecord/lib/active_record/associations/has_one_association.rb
  22. +2 −1  activerecord/lib/active_record/base.rb
  23. +15 −4 activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
  24. +10 −13 activerecord/test/cases/associations/belongs_to_associations_test.rb
  25. +41 −1 activerecord/test/cases/associations/eager_test.rb
  26. +4 −0 activerecord/test/cases/associations/has_many_associations_test.rb
  27. +10 −0 activerecord/test/cases/associations/has_one_associations_test.rb
  28. +10 −0 activerecord/test/cases/associations/has_one_through_associations_test.rb
  29. +0 −20 activerecord/test/cases/associations_test.rb
  30. +2 −0  activerecord/test/models/author.rb
  31. +6 −0 activerecord/test/models/club.rb
  32. +12 −1 activerecord/test/models/company.rb
  33. +1 −0  activerecord/test/models/post.rb
  34. +2 −0  activeresource/CHANGELOG
  35. +36 −0 activeresource/lib/active_resource/base.rb
  36. +1 −1  activeresource/lib/active_resource/formats/json_format.rb
  37. +17 −0 activeresource/test/format_test.rb
  38. +2 −0  activesupport/CHANGELOG
  39. +2 −1  activesupport/lib/active_support/core_ext/array/conversions.rb
  40. +2 −0  activesupport/lib/active_support/core_ext/float.rb
  41. +27 −0 activesupport/lib/active_support/core_ext/float/time.rb
  42. +2 −0  activesupport/lib/active_support/core_ext/integer.rb
  43. +45 −0 activesupport/lib/active_support/core_ext/integer/time.rb
  44. +0 −10 activesupport/lib/active_support/core_ext/numeric/time.rb
  45. +10 −0 activesupport/lib/active_support/core_ext/time/calculations.rb
  46. +26 −6 activesupport/lib/active_support/inflector.rb
  47. +1 −0  activesupport/lib/active_support/locale/en-US.yml
  48. +7 −3 activesupport/lib/active_support/memoizable.rb
  49. +2 −2 activesupport/lib/active_support/multibyte.rb
  50. +14 −2 activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
  51. +50 −0 activesupport/test/core_ext/duration_test.rb
  52. +8 −0 activesupport/test/core_ext/time_ext_test.rb
  53. +16 −0 activesupport/test/i18n_test.rb
  54. +2 −0  activesupport/test/inflector_test_cases.rb
  55. +12 −0 activesupport/test/json/encoding_test.rb
  56. +5 −0 activesupport/test/memoizable_test.rb
  57. +1 −1  railties/Rakefile
  58. +1 −52 railties/doc/guides/actioncontroller/rescue.txt
  59. +21 −0 railties/doc/guides/actioncontroller/session.txt
  60. +55 −1 railties/doc/guides/actionview/layouts_and_rendering.txt
  61. +2 −0  railties/doc/guides/activerecord/active_record_basics.txt
  62. +19 −18 railties/doc/guides/activerecord/association_basics.txt
  63. +0 −9 railties/doc/guides/benchmarking_and_profiling/appendix.txt
  64. +0 −55 railties/doc/guides/benchmarking_and_profiling/basics.txt
  65. +0 −22 railties/doc/guides/benchmarking_and_profiling/definitions.txt
  66. +239 −0 railties/doc/guides/benchmarking_and_profiling/index.txt
  67. +0 −44 railties/doc/guides/benchmarking_and_profiling/preamble.txt
  68. +939 −211 railties/doc/guides/getting_started_with_rails/getting_started_with_rails.txt
  69. +1 −3 railties/doc/guides/index.txt
  70. +5 −1 railties/doc/guides/routing/routing_outside_in.txt
  71. +4 −4 railties/doc/guides/securing_rails_applications/security.txt
  72. +437 −481 railties/doc/guides/testing_rails_applications/testing_rails_applications.txt
  73. +2 −0  railties/environments/environment.rb
  74. +2 −2 railties/lib/console_sandbox.rb
  75. +35 −4 railties/lib/rails/gem_dependency.rb
  76. +1 −1  railties/lib/rails/mongrel_server/commands.rb
  77. +68 −21 railties/lib/rails/vendor_gem_source_index.rb
  78. +1 −0  railties/lib/rails_generator/commands.rb
  79. +1 −1  railties/lib/rails_generator/generators/components/controller/controller_generator.rb
  80. +1 −1  railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb
  81. +1 −1  railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb
  82. +1 −1  railties/lib/rails_generator/generators/components/model/model_generator.rb
  83. +1 −1  railties/lib/rails_generator/generators/components/observer/observer_generator.rb
  84. +1 −1  railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
  85. +1 −1  railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
  86. +2 −2 railties/lib/rails_generator/generators/components/resource/resource_generator.rb
  87. +2 −2 railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
  88. +10 −0 railties/lib/tasks/gems.rake
  89. +20 −0 railties/test/gem_dependency_test.rb
  90. +3 −0  railties/test/generators/rails_controller_generator_test.rb
  91. +1 −0  railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb
  92. +28 −0 railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification
  93. +1 −0  railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb
View
10 actionpack/CHANGELOG
@@ -1,5 +1,15 @@
*Edge*
+* Added inline builder yield to atom_feed_helper tags where appropriate [Sam Ruby]. Example:
+
+ entry.summary :type => 'xhtml' do |xhtml|
+ xhtml.p pluralize(order.line_items.count, "line item")
+ xhtml.p "Shipped to #{order.address}"
+ xhtml.p "Paid by #{order.pay_type}"
+ end
+
+* Make PrototypeHelper#submit_to_remote a wrapper around PrototypeHelper#button_to_remote. [Tarmo Tänav]
+
* Set HttpOnly for the cookie session store's cookie. #1046
* Added FormTagHelper#image_submit_tag confirm option #784 [Alastair Brunton]
View
4 actionpack/lib/action_controller/base.rb
@@ -969,7 +969,9 @@ def head(*args)
# If-Modified-Since request header is <= last modified.
def last_modified!(utc_time)
response.last_modified= utc_time
- head(:not_modified) if response.last_modified == request.if_modified_since
+ if request.if_modified_since && request.if_modified_since <= utc_time
+ head(:not_modified)
+ end
end
# Sets the ETag response header. Returns 304 Not Modified if the
View
4 actionpack/lib/action_controller/routing/builder.rb
@@ -60,12 +60,10 @@ def segment_for(string)
# segments are passed alongside in order to distinguish between default values
# and requirements.
def divide_route_options(segments, options)
- options = options.dup
+ options = options.except(:path_prefix, :name_prefix)
if options[:namespace]
options[:controller] = "#{options.delete(:namespace).sub(/\/$/, '')}/#{options[:controller]}"
- options.delete(:path_prefix)
- options.delete(:name_prefix)
end
requirements = (options.delete(:requirements) || {}).dup
View
3  actionpack/lib/action_controller/test_case.rb
@@ -134,6 +134,9 @@ def setup_controller_request_and_response
@controller = self.class.controller_class.new
@controller.request = @request = TestRequest.new
@response = TestResponse.new
+
+ @controller.params = {}
+ @controller.send(:initialize_current_url)
end
# Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
View
2  actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -596,7 +596,7 @@ def compute_public_path(source)
end
def missing_extension?(source)
- extension && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, directory, "#{source}.#{extension}"))
+ extension && (File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, directory, "#{source}.#{extension}")))
end
def prepend_relative_url_root(source)
View
64 actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -51,6 +51,7 @@ module AtomFeedHelper
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
# 2005 is used (as an "I don't care" value).
+ # * <tt>:instruct</tt>: Hash of XML processing instructions in the form {target => {attribute => value, }} or {target => [{attribute => value, }, ]}
#
# Other namespaces can be added to the root element:
#
@@ -74,8 +75,20 @@ module AtomFeedHelper
# end
# end
#
+ # The Atom spec defines five elements (content rights title subtitle
+ # summary) which may directly contain xhtml content if :type => 'xhtml'
+ # is specified as an attribute. If so, this helper will take care of
+ # the enclosing div and xhtml namespace declaration. Example usage:
#
- # atom_feed yields an AtomFeedBuilder instance.
+ # entry.summary :type => 'xhtml' do |xhtml|
+ # xhtml.p pluralize(order.line_items.count, "line item")
+ # xhtml.p "Shipped to #{order.address}"
+ # xhtml.p "Paid by #{order.pay_type}"
+ # end
+ #
+ #
+ # atom_feed yields an AtomFeedBuilder instance. Nested elements yield
+ # an AtomBuilder instance.
def atom_feed(options = {}, &block)
if options[:schema_date]
options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
@@ -85,6 +98,15 @@ def atom_feed(options = {}, &block)
xml = options[:xml] || eval("xml", block.binding)
xml.instruct!
+ if options[:instruct]
+ options[:instruct].each do |target,attrs|
+ if attrs.respond_to?(:keys)
+ xml.instruct!(target, attrs)
+ elsif attrs.respond_to?(:each)
+ attrs.each { |attr_group| xml.instruct!(target, attr_group) }
+ end
+ end
+ end
feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'}
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
@@ -98,8 +120,38 @@ def atom_feed(options = {}, &block)
end
end
+ class AtomBuilder
+ def initialize(xml)
+ @xml = xml
+ end
+
+ private
+ # Delegate to xml builder, first wrapping the element in a xhtml
+ # namespaced div element if the method and arguments indicate
+ # that an xhtml_block? is desired.
+ def method_missing(method, *arguments, &block)
+ if xhtml_block?(method, arguments)
+ @xml.__send__(method, *arguments) do
+ @xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') do |xhtml|
+ block.call(xhtml)
+ end
+ end
+ else
+ @xml.__send__(method, *arguments, &block)
+ end
+ end
+
+ # True if the method name matches one of the five elements defined
+ # in the Atom spec as potentially containing XHTML content and
+ # if :type => 'xhtml' is, in fact, specified.
+ def xhtml_block?(method, arguments)
+ %w( content rights title subtitle summary ).include?(method.to_s) &&
+ arguments.last.respond_to?(:[]) &&
+ arguments.last[:type].to_s == 'xhtml'
+ end
+ end
- class AtomFeedBuilder
+ class AtomFeedBuilder < AtomBuilder
def initialize(xml, view, feed_options = {})
@xml, @view, @feed_options = xml, view, feed_options
end
@@ -131,15 +183,11 @@ def entry(record, options = {})
@xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:url] || @view.polymorphic_url(record))
- yield @xml
+ yield AtomBuilder.new(@xml)
end
end
-
- private
- def method_missing(method, *arguments, &block)
- @xml.__send__(method, *arguments, &block)
- end
end
+
end
end
end
View
7 actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -62,7 +62,7 @@ def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
# # <option>3</option><option>4</option></select>
#
# select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>", :multiple => true
- # # => <select id="colors" multiple="multiple" name="colors"><option>Red</option>
+ # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
# # <option>Green</option><option>Blue</option></select>
#
# select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>"
@@ -70,14 +70,15 @@ def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
# # <option>Out</option></select>
#
# select_tag "access", "<option>Read</option><option>Write</option>", :multiple => true, :class => 'form_input'
- # # => <select class="form_input" id="access" multiple="multiple" name="access"><option>Read</option>
+ # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
# # <option>Write</option></select>
#
# select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>", :disabled => true
# # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
# # <option>Paris</option><option>Rome</option></select>
def select_tag(name, option_tags = nil, options = {})
- content_tag :select, option_tags, { "name" => name, "id" => name }.update(options.stringify_keys)
+ html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
+ content_tag :select, option_tags, { "name" => html_name, "id" => name }.update(options.stringify_keys)
end
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
View
18 actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -405,7 +405,7 @@ def remote_form_for(record_or_name_or_array, *args, &proc)
# # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',
# # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Create" />
- # <%= button_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
+ # <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
#
# # Submit to the remote action update and update the DIV succeed or fail based
# # on the success or failure of the request
@@ -413,24 +413,18 @@ def remote_form_for(record_or_name_or_array, *args, &proc)
# # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},
# # '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Update" />
- # <%= button_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
+ # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
# :update => { :success => "succeed", :failure => "fail" }
#
# <tt>options</tt> argument is the same as in form_remote_tag.
- #
- # Note: This method used to be called submit_to_remote, but that's now just an alias for button_to_remote
- def button_to_remote(name, value, options = {})
+ def submit_to_remote(name, value, options = {})
options[:with] ||= 'Form.serialize(this.form)'
- options[:html] ||= {}
- options[:html][:type] = 'button'
- options[:html][:onclick] = "#{remote_function(options)}; return false;"
- options[:html][:name] = name
- options[:html][:value] = value
+ html_options = options.delete(:html) || {}
+ html_options[:name] = name
- tag("input", options[:html], false)
+ button_to_remote(value, options, html_options)
end
- alias_method :submit_to_remote, :button_to_remote
# Returns '<tt>eval(request.responseText)</tt>' which is the JavaScript function
# that +form_remote_tag+ can call in <tt>:complete</tt> to evaluate a multiple
View
6 actionpack/test/controller/render_test.rb
@@ -1441,6 +1441,12 @@ def test_request_with_bang_obeys_last_modified
get :conditional_hello_with_bangs
assert_response :not_modified
end
+
+ def test_last_modified_works_with_less_than_too
+ @request.if_modified_since = 5.years.ago.httpdate
+ get :conditional_hello_with_bangs
+ assert_response :not_modified
+ end
end
class RenderingLoggingTest < Test::Unit::TestCase
View
21 actionpack/test/controller/routing_test.rb
@@ -924,6 +924,20 @@ def test_named_route_with_default
end
+ def test_named_route_with_name_prefix
+ rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :name_prefix => 'my_'
+ x = setup_for_named_route
+ assert_equal("http://named.route.test/page",
+ x.send(:my_page_url))
+ end
+
+ def test_named_route_with_path_prefix
+ rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :path_prefix => 'my'
+ x = setup_for_named_route
+ assert_equal("http://named.route.test/my/page",
+ x.send(:page_url))
+ end
+
def test_named_route_with_nested_controller
rs.add_named_route :users, 'admin/user', :controller => 'admin/user', :action => 'index'
x = setup_for_named_route
@@ -2147,6 +2161,13 @@ def test_generate
assert_equal [:x], set.extra_keys(args)
end
+ def test_generate_with_path_prefix
+ set.draw { |map| map.connect ':controller/:action/:id', :path_prefix => 'my' }
+
+ args = { :controller => "foo", :action => "bar", :id => "7", :x => "y" }
+ assert_equal "/my/foo/bar/7?x=y", set.generate(args)
+ end
+
def test_named_routes_are_never_relative_to_modules
set.draw do |map|
map.connect "/connection/manage/:action", :controller => 'connection/manage'
View
1  actionpack/test/controller/test_test.rb
@@ -667,6 +667,7 @@ def test_should_be_able_to_use_named_routes_before_a_request_is_done
with_routing do |set|
set.draw { |map| map.resources :contents }
assert_equal 'http://test.host/contents/new', new_content_url
+ assert_equal 'http://test.host/contents/1', content_url(:id => 1)
end
end
end
View
13 actionpack/test/template/asset_tag_helper_test.rb
@@ -230,6 +230,19 @@ def test_image_tag
ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ uses_mocha 'test image tag with windows behaviour' do
+ def test_image_tag_windows_behaviour
+ old_asset_id, ENV["RAILS_ASSET_ID"] = ENV["RAILS_ASSET_ID"], "1"
+ # This simulates the behaviour of File#exist? on windows when testing a file ending in "."
+ # If the file "rails.png" exists, windows will return true when asked if "rails.png." exists (notice trailing ".")
+ # OS X, linux etc will return false in this case.
+ File.stubs(:exist?).with('template/../fixtures/public/images/rails.png.').returns(true)
+ assert_equal '<img alt="Rails" src="/images/rails.png?1" />', image_tag('rails.png')
+ ensure
+ ENV["RAILS_ASSET_ID"] = old_asset_id
+ end
+ end
+
def test_timebased_asset_id
expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
View
81 actionpack/test/template/atom_feed_helper_test.rb
@@ -92,6 +92,64 @@ class ScrollsController < ActionController::Base
end
end
EOT
+ FEEDS["feed_with_xml_processing_instructions"] = <<-EOT
+ atom_feed(:schema_date => '2008',
+ :instruct => {'xml-stylesheet' => { :href=> 't.css', :type => 'text/css' }}) do |feed|
+ feed.title("My great blog!")
+ feed.updated((@scrolls.first.created_at))
+
+ for scroll in @scrolls
+ feed.entry(scroll) do |entry|
+ entry.title(scroll.title)
+ entry.content(scroll.body, :type => 'html')
+
+ entry.author do |author|
+ author.name("DHH")
+ end
+ end
+ end
+ end
+ EOT
+ FEEDS["feed_with_xml_processing_instructions_duplicate_targets"] = <<-EOT
+ atom_feed(:schema_date => '2008',
+ :instruct => {'target1' => [{ :a => '1', :b => '2' }, { :c => '3', :d => '4' }]}) do |feed|
+ feed.title("My great blog!")
+ feed.updated((@scrolls.first.created_at))
+
+ for scroll in @scrolls
+ feed.entry(scroll) do |entry|
+ entry.title(scroll.title)
+ entry.content(scroll.body, :type => 'html')
+
+ entry.author do |author|
+ author.name("DHH")
+ end
+ end
+ end
+ end
+ EOT
+ FEEDS["feed_with_xhtml_content"] = <<-'EOT'
+ atom_feed do |feed|
+ feed.title("My great blog!")
+ feed.updated((@scrolls.first.created_at))
+
+ for scroll in @scrolls
+ feed.entry(scroll) do |entry|
+ entry.title(scroll.title)
+ entry.summary(:type => 'xhtml') do |xhtml|
+ xhtml.p "before #{scroll.id}"
+ xhtml.p {xhtml << scroll.body}
+ xhtml.p "after #{scroll.id}"
+ end
+ entry.tag!('app:edited', Time.now)
+
+ entry.author do |author|
+ author.name("DHH")
+ end
+ end
+ end
+ end
+ EOT
def index
@scrolls = [
Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)),
@@ -194,6 +252,29 @@ def test_feed_should_allow_overriding_ids
end
end
+ def test_feed_xml_processing_instructions
+ with_restful_routing(:scrolls) do
+ get :index, :id => 'feed_with_xml_processing_instructions'
+ assert_match %r{<\?xml-stylesheet type="text/css" href="t.css"\?>}, @response.body
+ end
+ end
+
+ def test_feed_xml_processing_instructions_duplicate_targets
+ with_restful_routing(:scrolls) do
+ get :index, :id => 'feed_with_xml_processing_instructions_duplicate_targets'
+ assert_match %r{<\?target1 (a="1" b="2"|b="2" a="1")\?>}, @response.body
+ assert_match %r{<\?target1 (c="3" d="4"|d="4" c="3")\?>}, @response.body
+ end
+ end
+
+ def test_feed_xhtml
+ with_restful_routing(:scrolls) do
+ get :index, :id => "feed_with_xhtml_content"
+ assert_match %r{xmlns="http://www.w3.org/1999/xhtml"}, @response.body
+ assert_select "summary div p", :text => "Something Boring"
+ assert_select "summary div p", :text => "after 2"
+ end
+ end
private
def with_restful_routing(resources)
with_routing do |set|
View
3  actionpack/test/template/form_tag_helper_test.rb
@@ -211,7 +211,8 @@ def test_label_tag_class_string
def test_boolean_optios
assert_dom_equal %(<input checked="checked" disabled="disabled" id="admin" name="admin" readonly="readonly" type="checkbox" value="1" />), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes")
assert_dom_equal %(<input checked="checked" id="admin" name="admin" type="checkbox" value="1" />), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil)
- assert_dom_equal %(<select id="people" multiple="multiple" name="people"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => true)
+ assert_dom_equal %(<select id="people" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => true)
+ assert_dom_equal %(<select id="people[]" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people[]", "<option>david</option>", :multiple => true)
assert_dom_equal %(<select id="people" name="people"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => nil)
end
View
6 actionpack/test/template/prototype_helper_test.rb
@@ -218,9 +218,9 @@ def test_on_callbacks
end
- def test_button_to_remote
- assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)}); return false;\" type=\"button\" value=\"1000000\" />),
- button_to_remote("More beer!", 1_000_000, :update => "empty_bottle")
+ def test_submit_to_remote
+ assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});\" type=\"button\" value=\"1000000\" />),
+ submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle")
end
def test_observe_field
View
3  activerecord/lib/active_record/association_preload.rb
@@ -193,6 +193,7 @@ def preload_has_and_belongs_to_many_association(records, reflection, preload_opt
end
def preload_has_one_association(records, reflection, preload_options={})
+ return if records.first.send("loaded_#{reflection.name}?")
id_to_record_map, ids = construct_id_map(records)
options = reflection.options
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
@@ -214,6 +215,7 @@ def preload_has_one_association(records, reflection, preload_options={})
end
def preload_has_many_association(records, reflection, preload_options={})
+ return if records.first.send(reflection.name).loaded?
options = reflection.options
primary_key_name = reflection.through_reflection_primary_key_name
@@ -271,6 +273,7 @@ def preload_through_records(records, reflection, through_association)
end
def preload_belongs_to_association(records, reflection, preload_options={})
+ return if records.first.send("loaded_#{reflection.name}?")
options = reflection.options
primary_key_name = reflection.primary_key_name
View
21 activerecord/lib/active_record/associations.rb
@@ -624,7 +624,8 @@ module ClassMethods
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
# [collection.delete(object, ...)]
# Removes one or more objects from the collection by setting their foreign keys to +NULL+.
- # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
+ # Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
+ # and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
# [collection=objects]
# Replaces the collections content by deleting and adding objects as appropriate.
# [collection_singular_ids]
@@ -1235,7 +1236,7 @@ def association_accessor_methods(reflection, association_proxy_class)
association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
- if association.nil? || !association.loaded? || force_reload
+ if association.nil? || force_reload
association = association_proxy_class.new(self, reflection)
retval = association.reload
if retval.nil? and association_proxy_class == BelongsToAssociation
@@ -1248,6 +1249,11 @@ def association_accessor_methods(reflection, association_proxy_class)
association.target.nil? ? nil : association
end
+ define_method("loaded_#{reflection.name}?") do
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association && association.loaded?
+ end
+
define_method("#{reflection.name}=") do |new_value|
association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
@@ -1264,17 +1270,6 @@ def association_accessor_methods(reflection, association_proxy_class)
end
end
- if association_proxy_class == BelongsToAssociation
- define_method("#{reflection.primary_key_name}=") do |target_id|
- if instance_variable_defined?(ivar)
- if association = instance_variable_get(ivar)
- association.reset
- end
- end
- write_attribute(reflection.primary_key_name, target_id)
- end
- end
-
define_method("set_#{reflection.name}_target") do |target|
return if target.nil? and association_proxy_class == BelongsToAssociation
association = association_proxy_class.new(self, reflection)
View
12 activerecord/lib/active_record/associations/association_collection.rb
@@ -183,7 +183,13 @@ def count(*args)
end
- # Remove +records+ from this association. Does not destroy +records+.
+ # Removes +records+ from this association calling +before_remove+ and
+ # +after_remove+ callbacks.
+ #
+ # This method is abstract in the sense that +delete_records+ has to be
+ # provided by descendants. Note this method does not imply the records
+ # are actually removed from the database, that depends precisely on
+ # +delete_records+. They are in any case removed from the collection.
def delete(*records)
records = flatten_deeper(records)
records.each { |record| raise_on_type_mismatch(record) }
@@ -320,6 +326,10 @@ def include?(record)
exists?(record)
end
+ def proxy_respond_to?(method)
+ super || @reflection.klass.respond_to?(method)
+ end
+
protected
def construct_find_options!(options)
end
View
11 activerecord/lib/active_record/associations/association_proxy.rb
@@ -140,6 +140,15 @@ def inspect
@target.inspect
end
+ def send(method, *args)
+ if proxy_respond_to?(method)
+ super
+ else
+ load_target
+ @target.send(method, *args)
+ end
+ end
+
protected
# Does the association have a <tt>:dependent</tt> option?
def dependent?
@@ -197,6 +206,8 @@ def with_scope(*args, &block)
# Forwards any missing method call to the \target.
def method_missing(method, *args)
if load_target
+ raise NoMethodError unless @target.respond_to?(method)
+
if block_given?
@target.send(method, *args) { |*block_args| yield(*block_args) }
else
View
1  activerecord/lib/active_record/associations/has_many_association.rb
@@ -61,6 +61,7 @@ def insert_record(record)
record.save
end
+ # Deletes the records according to the <tt>:dependent</tt> option.
def delete_records(records)
case @reflection.options[:dependent]
when :destroy
View
2  activerecord/lib/active_record/associations/has_one_association.rb
@@ -57,7 +57,7 @@ def replace(obj, dont_save = false)
protected
def owner_quoted_id
if @reflection.options[:primary_key]
- quote_value(@owner.send(@reflection.options[:primary_key]))
+ @owner.class.quote_value(@owner.send(@reflection.options[:primary_key]))
else
@owner.quoted_id
end
View
3  activerecord/lib/active_record/base.rb
@@ -514,7 +514,7 @@ class << self # Class methods
#
# ==== Parameters
#
- # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or <tt>[ "user_name = ?", username ]</tt>. See conditions in the intro.
+ # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>, or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
# * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
@@ -551,6 +551,7 @@ class << self # Class methods
# # find first
# Person.find(:first) # returns the first object fetched by SELECT * FROM people
# Person.find(:first, :conditions => [ "user_name = ?", user_name])
+ # Person.find(:first, :conditions => [ "user_name = :u", { :u => user_name }])
# Person.find(:first, :order => "created_on DESC", :offset => 5)
#
# # find last
View
19 activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -120,10 +120,6 @@ def add_limit_offset!(sql, options)
sql
end
- def sanitize_limit(limit)
- limit.to_s[/,/] ? limit.split(',').map{ |i| i.to_i }.join(',') : limit.to_i
- end
-
# Appends a locking clause to an SQL statement.
# This method *modifies* the +sql+ parameter.
# # SELECT * FROM suppliers FOR UPDATE
@@ -185,6 +181,21 @@ def update_sql(sql, name = nil)
def delete_sql(sql, name = nil)
update_sql(sql, name)
end
+
+ # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
+ #
+ # +limit+ may be anything that can evaluate to a string via #to_s. It
+ # should look like an integer, or a comma-delimited list of integers.
+ #
+ # Returns the sanitized limit parameter, either as an integer, or as a
+ # string which contains a comma-delimited list of integers.
+ def sanitize_limit(limit)
+ if limit.to_s =~ /,/
+ limit.to_s.split(',').map{ |i| i.to_i }.join(',')
+ else
+ limit.to_i
+ end
+ end
end
end
end
View
23 activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -47,19 +47,6 @@ def test_natural_assignment
assert_equal apple.id, citibank.firm_id
end
- def test_foreign_key_assignment
- # Test using an existing record
- signals37 = accounts(:signals37)
- assert_equal companies(:first_firm), signals37.firm
- signals37.firm_id = companies(:another_firm).id
- assert_equal companies(:another_firm), signals37.firm
-
- # Test using a new record
- account = Account.new
- account.firm_id = companies(:another_firm).id
- assert_equal companies(:another_firm), account.firm
- end
-
def test_no_unexpected_aliasing
first_firm = companies(:first_firm)
another_firm = companies(:another_firm)
@@ -441,4 +428,14 @@ def test_save_succeeds_for_invalid_belongs_to_with_validate_false
assert log.valid?
assert log.save
end
+
+ def test_belongs_to_proxy_should_not_respond_to_private_methods
+ assert_raises(NoMethodError) { companies(:first_firm).private_method }
+ assert_raises(NoMethodError) { companies(:second_client).firm.private_method }
+ end
+
+ def test_belongs_to_proxy_should_respond_to_private_methods_via_send
+ companies(:first_firm).send(:private_method)
+ companies(:second_client).firm.send(:private_method)
+ end
end
View
42 activerecord/test/cases/associations/eager_test.rb
@@ -18,7 +18,7 @@
require 'models/project'
class EagerAssociationTest < ActiveRecord::TestCase
- fixtures :posts, :comments, :authors, :categories, :categories_posts,
+ fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts,
:companies, :accounts, :tags, :taggings, :people, :readers,
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
:developers, :projects, :developers_projects
@@ -111,6 +111,46 @@ def test_including_duplicate_objects_from_has_many
end
end
+ def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
+ author_id = authors(:david).id
+ author = assert_queries(3) { Author.find(author_id, :include => {:posts_with_comments => :comments}) } # find the author, then find the posts, then find the comments
+ author.posts_with_comments.each do |post_with_comments|
+ assert_equal post_with_comments.comments.length, post_with_comments.comments.count
+ assert_equal nil, post_with_comments.comments.uniq!
+ end
+ end
+
+ def test_finding_with_includes_on_has_one_assocation_with_same_include_includes_only_once
+ author = authors(:david)
+ post = author.post_about_thinking_with_last_comment
+ last_comment = post.last_comment
+ author = assert_queries(3) { Author.find(author.id, :include => {:post_about_thinking_with_last_comment => :last_comment})} # find the author, then find the posts, then find the comments
+ assert_no_queries do
+ assert_equal post, author.post_about_thinking_with_last_comment
+ assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
+ end
+ end
+
+ def test_finding_with_includes_on_belongs_to_association_with_same_include_includes_only_once
+ post = posts(:welcome)
+ author = post.author
+ author_address = author.author_address
+ post = assert_queries(3) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author, then find the address
+ assert_no_queries do
+ assert_equal author, post.author_with_address
+ assert_equal author_address, post.author_with_address.author_address
+ end
+ end
+
+ def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
+ post = posts(:welcome)
+ post.update_attributes!(:author => nil)
+ post = assert_queries(2) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the address
+ assert_no_queries do
+ assert_equal nil, post.author_with_address
+ end
+ end
+
def test_loading_from_an_association
posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
assert_equal 2, posts.first.comments.size
View
4 activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1080,5 +1080,9 @@ def test_association_proxy_transaction_method_starts_transaction_in_association_
end
end
+ def test_sending_new_to_association_proxy_should_have_same_effect_as_calling_new
+ clients_assoc = companies(:first_firm).clients
+ assert_equal clients_assoc.new.attributes, clients_assoc.send(:new).attributes
+ end
end
View
10 activerecord/test/cases/associations/has_one_associations_test.rb
@@ -349,4 +349,14 @@ def test_cant_save_readonly_association
assert companies(:first_firm).readonly_account.readonly?
end
+ def test_has_one_proxy_should_not_respond_to_private_methods
+ assert_raises(NoMethodError) { accounts(:signals37).private_method }
+ assert_raises(NoMethodError) { companies(:first_firm).account.private_method }
+ end
+
+ def test_has_one_proxy_should_respond_to_private_methods_via_send
+ accounts(:signals37).send(:private_method)
+ companies(:first_firm).account.send(:private_method)
+ end
+
end
View
10 activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -110,4 +110,14 @@ def test_assigning_association_correctly_assigns_target
new_member.club = new_club = Club.create(:name => "LRUG")
assert_equal new_club, new_member.club.target
end
+
+ def test_has_one_through_proxy_should_not_respond_to_private_methods
+ assert_raises(NoMethodError) { clubs(:moustache_club).private_method }
+ assert_raises(NoMethodError) { @member.club.private_method }
+ end
+
+ def test_has_one_through_proxy_should_respond_to_private_methods_via_send
+ clubs(:moustache_club).send(:private_method)
+ @member.club.send(:private_method)
+ end
end
View
20 activerecord/test/cases/associations_test.rb
@@ -182,26 +182,6 @@ def test_failed_reset_returns_nil
assert_nil p.author.reset
end
- def test_reset_loads_association_next_time
- welcome = posts(:welcome)
- david = authors(:david)
- author_assoc = welcome.author
-
- assert_equal david, welcome.author # So we can be sure the test works correctly
- author_assoc.reset
- assert !author_assoc.loaded?
- assert_nil author_assoc.target
- assert_equal david, welcome.author
- end
-
- def test_assigning_association_id_after_reload
- welcome = posts(:welcome)
- welcome.reload
- assert_nothing_raised do
- welcome.author_id = authors(:david).id
- end
- end
-
def test_reload_returns_assocition
david = developers(:david)
assert_nothing_raised do
View
2  activerecord/test/models/author.rb
@@ -17,6 +17,8 @@ def testing_proxy_target
proxy_target
end
end
+ has_one :post_about_thinking, :class_name => 'Post', :conditions => "posts.title like '%thinking%'"
+ has_one :post_about_thinking_with_last_comment, :class_name => 'Post', :conditions => "posts.title like '%thinking%'", :include => :last_comment
has_many :comments, :through => :posts
has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'"
View
6 activerecord/test/models/club.rb
@@ -4,4 +4,10 @@ class Club < ActiveRecord::Base
has_many :current_memberships
has_one :sponsor
has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member"
+
+ private
+
+ def private_method
+ "I'm sorry sir, this is a *private* club, not a *pirate* club"
+ end
end
View
13 activerecord/test/models/company.rb
@@ -13,6 +13,12 @@ class Company < AbstractCompany
def arbitrary_method
"I am Jack's profound disappointment"
end
+
+ private
+
+ def private_method
+ "I am Jack's innermost fears and aspirations"
+ end
end
module Namespaced
@@ -129,9 +135,14 @@ def self.destroyed_account_ids
true
end
-
protected
def validate
errors.add_on_empty "credit_limit"
end
+
+ private
+
+ def private_method
+ "Sir, yes sir!"
+ end
end
View
1  activerecord/test/models/post.rb
@@ -13,6 +13,7 @@ def greeting
end
belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
+ belongs_to :author_with_address, :class_name => "Author", :foreign_key => :author_id, :include => :author_address
has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
View
2  activeresource/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Add ActiveResource::Base#to_xml and ActiveResource::Base#to_json. #1011 [Rasik Pandey, Cody Fauser]
+
* Add ActiveResource::Base.find(:last). [#754 state:resolved] (Adrian Mugnolo)
* Fixed problems with the logger used if the logging string included %'s [#840 state:resolved] (Jamis Buck)
View
36 activeresource/lib/active_resource/base.rb
@@ -854,6 +854,42 @@ def exists?
#
# my_group.to_xml(:skip_instruct => true)
# # => <subsidiary_group> [...] </subsidiary_group>
+ def to_xml(options={})
+ attributes.to_xml({:root => self.class.element_name}.merge(options))
+ end
+
+ # Returns a JSON string representing the model. Some configuration is
+ # available through +options+.
+ #
+ # ==== Options
+ # The +options+ are passed to the +to_json+ method on each
+ # attribute, so the same options as the +to_json+ methods in
+ # Active Support.
+ #
+ # * <tt>:only</tt> - Only include the specified attribute or list of
+ # attributes in the serialized output. Attribute names must be specified
+ # as strings.
+ # * <tt>:except</tt> - Do not include the specified attribute or list of
+ # attributes in the serialized output. Attribute names must be specified
+ # as strings.
+ #
+ # ==== Examples
+ # person = Person.new(:first_name => "Jim", :last_name => "Smith")
+ # person.to_json
+ # # => {"first_name": "Jim", "last_name": "Smith"}
+ #
+ # person.to_json(:only => ["first_name"])
+ # # => {"first_name": "Jim"}
+ #
+ # person.to_json(:except => ["first_name"])
+ # # => {"last_name": "Smith"}
+ def to_json(options={})
+ attributes.to_json(options)
+ end
+
+ # Returns the serialized string representation of the resource in the configured
+ # serialization format specified in ActiveResource::Base.format. The options
+ # applicable depend on the configured encoding format.
def encode(options={})
case self.class.format
when ActiveResource::Formats[:xml]
View
2  activeresource/lib/active_resource/formats/json_format.rb
@@ -12,7 +12,7 @@ def mime_type
end
def encode(hash, options={})
- hash.to_json
+ hash.to_json(options)
end
def decode(json)
View
17 activeresource/test/format_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require "fixtures/person"
+require "fixtures/street_address"
class FormatTest < Test::Unit::TestCase
def setup
@@ -83,6 +84,22 @@ def test_setting_format_before_site
assert_equal ActiveResource::Formats[:json], resource.connection.format
end
+ def test_serialization_of_nested_resource
+ address = { :street => '12345 Street' }
+ person = { :name=> 'Rus', :address => address}
+
+ [:json, :xml].each do |format|
+ encoded_person = ActiveResource::Formats[format].encode(person)
+ assert_match /12345 Street/, encoded_person
+ remote_person = Person.new(person.update({:address => StreetAddress.new(address)}))
+ assert_kind_of StreetAddress, remote_person.address
+ using_format(Person, format) do
+ ActiveResource::HttpMock.respond_to.post "/people.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, encoded_person, 201, {'Location' => "/people/5.#{format}"}
+ remote_person.save
+ end
+ end
+ end
+
private
def using_format(klass, mime_type_reference)
previous_format = klass.format
View
2  activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Time#advance recognizes fractional days and weeks. Deprecate Durations of fractional months and years #970 [Tom Lea]
+
* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik]
* Switch from String#chars to String#mb_chars for the unicode proxy. [Manfred Stienstra]
View
3  activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -11,7 +11,8 @@ def to_sentence(options = {})
options.assert_valid_keys(:connector, :skip_last_comma, :locale)
default = I18n.translate(:'support.array.sentence_connector', :locale => options[:locale])
- options.reverse_merge! :connector => default, :skip_last_comma => false
+ default_skip_last_comma = I18n.translate(:'support.array.skip_last_comma', :locale => options[:locale])
+ options.reverse_merge! :connector => default, :skip_last_comma => default_skip_last_comma
options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == ''
case length
View
2  activesupport/lib/active_support/core_ext/float.rb
@@ -1,5 +1,7 @@
require 'active_support/core_ext/float/rounding'
+require 'active_support/core_ext/float/time'
class Float #:nodoc:
include ActiveSupport::CoreExtensions::Float::Rounding
+ include ActiveSupport::CoreExtensions::Float::Time
end
View
27 activesupport/lib/active_support/core_ext/float/time.rb
@@ -0,0 +1,27 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Float #:nodoc:
+ module Time
+ # Deprication helper methods not available as core_ext is loaded first.
+ def years
+ ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:years, "Fractional years are not respected. Convert value to integer before calling #years."), caller)
+ years_without_deprecation
+ end
+ def months
+ ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:months, "Fractional months are not respected. Convert value to integer before calling #months."), caller)
+ months_without_deprecation
+ end
+
+ def months_without_deprecation
+ ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
+ end
+ alias :month :months
+
+ def years_without_deprecation
+ ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
+ end
+ alias :year :years
+ end
+ end
+ end
+end
View
2  activesupport/lib/active_support/core_ext/integer.rb
@@ -1,7 +1,9 @@
require 'active_support/core_ext/integer/even_odd'
require 'active_support/core_ext/integer/inflections'
+require 'active_support/core_ext/integer/time'
class Integer #:nodoc:
include ActiveSupport::CoreExtensions::Integer::EvenOdd
include ActiveSupport::CoreExtensions::Integer::Inflections
+ include ActiveSupport::CoreExtensions::Integer::Time
end
View
45 activesupport/lib/active_support/core_ext/integer/time.rb
@@ -0,0 +1,45 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Integer #:nodoc:
+ # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
+ #
+ # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
+ # as well as adding or subtracting their results from a Time object. For example:
+ #
+ # # equivalent to Time.now.advance(:months => 1)
+ # 1.month.from_now
+ #
+ # # equivalent to Time.now.advance(:years => 2)
+ # 2.years.from_now
+ #
+ # # equivalent to Time.now.advance(:months => 4, :years => 5)
+ # (4.months + 5.years).from_now
+ #
+ # While these methods provide precise calculation when used as in the examples above, care
+ # should be taken to note that this is not true if the result of `months', `years', etc is
+ # converted before use:
+ #
+ # # equivalent to 30.days.to_i.from_now
+ # 1.month.to_i.from_now
+ #
+ # # equivalent to 365.25.days.to_f.from_now
+ # 1.year.to_f.from_now
+ #
+ # In such cases, Ruby's core
+ # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
+ # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
+ # date and time arithmetic
+ module Time
+ def months
+ ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
+ end
+ alias :month :months
+
+ def years
+ ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
+ end
+ alias :year :years
+ end
+ end
+ end
+end
View
10 activesupport/lib/active_support/core_ext/numeric/time.rb
@@ -60,16 +60,6 @@ def fortnights
end
alias :fortnight :fortnights
- def months
- ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
- end
- alias :month :months
-
- def years
- ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
- end
- alias :year :years
-
# Reads best without arguments: 10.minutes.ago
def ago(time = ::Time.now)
time - self
View
10 activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -98,6 +98,16 @@ def change(options)
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
# <tt>:minutes</tt>, <tt>:seconds</tt>.
def advance(options)
+ unless options[:weeks].nil?
+ options[:weeks], partial_weeks = options[:weeks].divmod(1)
+ options[:days] = (options[:days] || 0) + 7 * partial_weeks
+ end
+
+ unless options[:days].nil?
+ options[:days], partial_days = options[:days].divmod(1)
+ options[:hours] = (options[:hours] || 0) + 24 * partial_days
+ end
+
d = to_date.advance(options)
time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
View
32 activesupport/lib/active_support/inflector.rb
@@ -1,4 +1,5 @@
require 'singleton'
+require 'iconv'
module ActiveSupport
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
@@ -258,12 +259,31 @@ def demodulize(class_name_in_module)
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize(string, sep = '-')
re_sep = Regexp.escape(sep)
- string.mb_chars.normalize(:kd). # Decompose accented characters
- gsub(/[^\x00-\x7F]+/, ''). # Remove anything non-ASCII entirely (e.g. diacritics).
- gsub(/[^a-z0-9\-_\+]+/i, sep). # Turn unwanted chars into the separator.
- squeeze(sep). # No more than one of the separator in a row.
- gsub(/^#{re_sep}|#{re_sep}$/i, ''). # Remove leading/trailing separator.
- downcase
+ # replace accented chars with ther ascii equivalents
+ parameterized_string = transliterate(string)
+ # Turn unwanted chars into the seperator
+ parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep)
+ # No more than one of the separator in a row.
+ parameterized_string.squeeze!(sep)
+ # Remove leading/trailing separator.
+ parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
+ parameterized_string.downcase
+ end
+
+
+ # Replaces accented characters with their ascii equivalents.
+ def transliterate(string)
+ Iconv.iconv('ascii//ignore//translit', 'utf-8', string).to_s
+ end
+
+ # The iconv transliteration code doesn't function correctly
+ # on some platforms, but it's very fast where it does function.
+ if "foo" != Inflector.transliterate("föö")
+ undef_method :transliterate
+ def transliterate(string)
+ string.mb_chars.normalize(:kd). # Decompose accented characters
+ gsub(/[^\x00-\x7F]+/, '') # Remove anything non-ASCII entirely (e.g. diacritics).
+ end
end
# Create the name of a table like Rails does for models to table names. This method
View
1  activesupport/lib/active_support/locale/en-US.yml
@@ -29,3 +29,4 @@ en-US:
support:
array:
sentence_connector: "and"
+ skip_last_comma: false
View
10 activesupport/lib/active_support/memoizable.rb
@@ -1,5 +1,9 @@
module ActiveSupport
module Memoizable
+ MEMOIZED_IVAR = Proc.new do |symbol|
+ "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
+ end
+
module Freezable
def self.included(base)
base.class_eval do
@@ -20,7 +24,7 @@ def memoize_all
if method(m).arity == 0
__send__($1)
else
- ivar = :"@_memoized_#{$1}"
+ ivar = MEMOIZED_IVAR.call($1)
instance_variable_set(ivar, {})
end
end
@@ -30,7 +34,7 @@ def memoize_all
def unmemoize_all
methods.each do |m|
if m.to_s =~ /^_unmemoized_(.*)/
- ivar = :"@_memoized_#{$1}"
+ ivar = MEMOIZED_IVAR.call($1)
instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
end
end
@@ -40,7 +44,7 @@ def unmemoize_all
def memoize(*symbols)
symbols.each do |symbol|
original_method = :"_unmemoized_#{symbol}"
- memoized_ivar = :"@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}"
+ memoized_ivar = MEMOIZED_IVAR.call(symbol)
class_eval <<-EOS, __FILE__, __LINE__
include Freezable
View
4 activesupport/lib/active_support/multibyte.rb
@@ -8,13 +8,13 @@ module ActiveSupport #:nodoc:
module Multibyte
# A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
# information about normalization.
- NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
+ NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
# The Unicode version that is supported by the implementation
UNICODE_VERSION = '5.1.0'
# The default normalization used for operations that require normalization. It can be set to any of the
- # normalizations in NORMALIZATIONS_FORMS.
+ # normalizations in NORMALIZATION_FORMS.
#
# Example:
# ActiveSupport::Multibyte.default_normalization_form = :c
View
16 activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
@@ -30,7 +30,13 @@ def translate(locale, key, options = {})
options.delete(:default)
values = options.reject{|name, value| reserved.include? name }
- entry = lookup(locale, key, scope) || default(locale, default, options) || raise(I18n::MissingTranslationData.new(locale, key, options))
+ entry = lookup(locale, key, scope)
+ if entry.nil?
+ entry = default(locale, default, options)
+ if entry.nil?
+ raise(I18n::MissingTranslationData.new(locale, key, options))
+ end
+ end
entry = pluralize locale, entry, count
entry = interpolate locale, entry, values
entry
@@ -83,7 +89,13 @@ def lookup(locale, key, scope = [])
return unless key
init_translations unless initialized?
keys = I18n.send :normalize_translation_keys, locale, key, scope
- keys.inject(translations){|result, k| result[k.to_sym] or return nil }
+ keys.inject(translations) do |result, k|
+ if (x = result[k.to_sym]).nil?
+ return nil
+ else
+ x
+ end
+ end
end
# Evaluates a default translation.
View
50 activesupport/test/core_ext/duration_test.rb
@@ -30,7 +30,57 @@ def test_argument_error
end
end
+ def test_fractional_weeks
+ assert_equal (86400 * 7) * 1.5, 1.5.weeks
+ assert_equal (86400 * 7) * 1.7, 1.7.weeks
+ end
+
+ def test_fractional_days
+ assert_equal 86400 * 1.5, 1.5.days
+ assert_equal 86400 * 1.7, 1.7.days
+ end
+
uses_mocha 'TestDurationSinceAndAgoWithCurrentTime' do
+ def test_since_and_ago_with_fractional_days
+ Time.stubs(:now).returns Time.local(2000)
+ # since
+ assert_equal 36.hours.since, 1.5.days.since
+ assert_equal((24 * 1.7).hours.since, 1.7.days.since)
+ # ago
+ assert_equal 36.hours.ago, 1.5.days.ago
+ assert_equal((24 * 1.7).hours.ago, 1.7.days.ago)
+ end
+
+ def test_since_and_ago_with_fractional_weeks
+ Time.stubs(:now).returns Time.local(2000)
+ # since
+ assert_equal((7 * 36).hours.since, 1.5.weeks.since)
+ assert_equal((7 * 24 * 1.7).hours.since, 1.7.weeks.since)
+ # ago
+ assert_equal((7 * 36).hours.ago, 1.5.weeks.ago)
+ assert_equal((7 * 24 * 1.7).hours.ago, 1.7.weeks.ago)
+ end
+
+ def test_deprecated_fractional_years
+ years_re = /Fractional years are not respected\. Convert value to integer before calling #years\./
+ assert_deprecated(years_re){1.0.years}
+ assert_deprecated(years_re){1.5.years}
+ assert_not_deprecated{1.years}
+ assert_deprecated(years_re){1.0.year}
+ assert_deprecated(years_re){1.5.year}
+ assert_not_deprecated{1.year}
+ end
+
+ def test_deprecated_fractional_months
+ months_re = /Fractional months are not respected\. Convert value to integer before calling #months\./
+ assert_deprecated(months_re){1.5.months}
+ assert_deprecated(months_re){1.0.months}
+ assert_not_deprecated{1.months}
+ assert_deprecated(months_re){1.5.month}
+ assert_deprecated(months_re){1.0.month}
+ assert_not_deprecated{1.month}
+ end
+
def test_since_and_ago_anchored_to_time_now_when_time_zone_default_not_set
Time.zone_default = nil
with_env_tz 'US/Eastern' do
View
8 activesupport/test/core_ext/time_ext_test.rb
@@ -381,7 +381,11 @@ def test_advance
assert_equal Time.local(2006,2,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 1)
assert_equal Time.local(2005,6,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:months => 4)
assert_equal Time.local(2005,3,21,15,15,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3)
+ assert_equal Time.local(2005,3,25,3,15,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.5)
+ assert_equal Time.local(2005,3,26,12,51,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.7)
assert_equal Time.local(2005,3,5,15,15,10), Time.local(2005,2,28,15,15,10).advance(:days => 5)
+ assert_equal Time.local(2005,3,6,3,15,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.5)
+ assert_equal Time.local(2005,3,6,8,3,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.7)
assert_equal Time.local(2012,9,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 7)
assert_equal Time.local(2013,10,3,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :days => 5)
assert_equal Time.local(2013,10,17,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
@@ -399,7 +403,11 @@ def test_utc_advance
assert_equal Time.utc(2006,2,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 1)
assert_equal Time.utc(2005,6,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:months => 4)
assert_equal Time.utc(2005,3,21,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3)
+ assert_equal Time.utc(2005,3,25,3,15,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.5)
+ assert_equal Time.utc(2005,3,26,12,51,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.7)
assert_equal Time.utc(2005,3,5,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5)
+ assert_equal Time.utc(2005,3,6,3,15,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.5)
+ assert_equal Time.utc(2005,3,6,8,3,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.7)
assert_equal Time.utc(2012,9,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 7)
assert_equal Time.utc(2013,10,3,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 19, :days => 11)
assert_equal Time.utc(2013,10,17,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
View
16 activesupport/test/i18n_test.rb
@@ -72,4 +72,20 @@ def test_time_am
def test_time_pm
assert_equal 'pm', I18n.translate(:'time.pm')
end
+
+ def test_sentence_connector
+ assert_equal 'and', I18n.translate(:'support.array.sentence_connector')
+ end
+
+ def test_skip_last_comma
+ assert_equal false, I18n.translate(:'support.array.skip_last_comma')
+ end
+
+ def test_to_sentence
+ assert_equal 'a, b, and c', %w[a b c].to_sentence
+ I18n.backend.store_translations 'en-US', :support => { :array => { :skip_last_comma => true } }
+ assert_equal 'a, b and c', %w[a b c].to_sentence
+ ensure
+ I18n.backend.store_translations 'en-US', :support => { :array => { :skip_last_comma => false } }
+ end
end
View
2  activesupport/test/inflector_test_cases.rb
@@ -1,3 +1,5 @@
+# encoding: utf-8
+
module InflectorTestCases
SingularToPlural = {
"search" => "searches",
View
12 activesupport/test/json/encoding_test.rb
@@ -100,6 +100,18 @@ def test_time_to_json_includes_local_offset
ActiveSupport.use_standard_json_time_format = false
end
+ def test_nested_hash_with_float
+ assert_nothing_raised do
+ hash = {
+ "CHI" => {
+ :dislay_name => "chicago",
+ :latitude => 123.234
+ }
+ }
+ result = hash.to_json
+ end
+ end
+
protected
def object_keys(json_object)
View
5 activesupport/test/memoizable_test.rb
@@ -100,6 +100,11 @@ def test_memoization
def test_memoization_with_punctuation
assert_equal true, @person.name?
+
+ assert_nothing_raised(NameError) do
+ @person.memoize_all
+ @person.unmemoize_all
+ end
end
def test_memoization_with_nil_value
View
2  railties/Rakefile
@@ -288,7 +288,7 @@ guides = [
{ 'activerecord' => 'finders' },
{ 'debugging' => 'debugging_rails_applications' },
{ 'caching' => 'caching_with_rails' },
- { 'benchmarking_and_profiling' => 'preamble' },
+ { 'benchmarking_and_profiling' => 'index' },
{ 'actionview' => 'layouts_and_rendering' }
]
View
53 railties/doc/guides/actioncontroller/rescue.txt
@@ -64,55 +64,4 @@ private
end
-----------------------------------
-NOTE: Certain exceptions are only rescuable from the ApplicationController class, as they are raised before the controller gets initialized and the action gets executed. See Partik Naik's link:http://m.onkey.org/2008/7/20/rescue-from-dispatching[article] on the subject for more information.
-
-=== `rescue_action` ===
-
-The `rescue_from` method was added to make it easier to rescue different kinds of exceptions and deal with each separately. Action Controller has a default method which intercepts *all* exceptions raised, `rescue_action`. You can override this method in a controller or in ApplicationController to rescue all exceptions raised in that particular context. You can get a little bit more granular by using the link:http://api.rubyonrails.org/classes/ActionController/Rescue.html#M000615[rescue_action_in_public] and link:http://api.rubyonrails.org/classes/ActionController/Rescue.html#M000618[rescue_action_locally] methods which are used to rescue actions for public and local requests. Let's see how the User::NotAuthorized exception could be caught using this technique:
-
-[source, ruby]
-----------------------------------------
-class ApplicationController < ActionController::Base
-
-private
-
- def rescue_action_in_public(exception)
- case exception
- when User::NotAuthorized
- user_not_authorized
- else
- super
- end
- end
-
-end
-----------------------------------------
-
-As you can see, this gets a bit messy once you start rescuing various types of error that require separate handlers, so it's a good idea to use `rescue_from` instead.
-
-=== Getting down and dirty ===
-
-Of course you can still use Ruby's `rescue` to rescue exceptions wherever you want. This is usually constrained to single methods, i.e. actions, but is still a very useful technique that should be used when appropriate. For example, you might use an API that raises a timeout error in one of your actions, and you have to handle that if it's raised:
-
-[source, ruby]
-----------------------------------------
-require 'clients_international'
-class ClientsController < ApplicationController
-
- def update
- @client = Client.find(params[:id])
- @client.attributes = params[:client]
- if @client.save
- flash[:notice] = "Client was updated"
- ClientsInternational.send_update(@client.to_xml)
- redirect_to clients_url
- else
- render :action => "new"
- end
- rescue ClientsInternational::TimeoutError
- flash[:error] = "Couldn't send API update"
- redirect_to @client
- end
-
-end
-----------------------------------------
+NOTE: Certain exceptions are only rescuable from the ApplicationController class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's link:http://m.onkey.org/2008/7/20/rescue-from-dispatching[article] on the subject for more information.
View
21 railties/doc/guides/actioncontroller/session.txt
@@ -162,3 +162,24 @@ class MainController < ApplicationController
end
------------------------------------------
+
+==== flash.now ====
+
+By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the `create` action fails to save a resource and you render the `new` template directly, that's not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use `flash.now` in the same way you use the normal `flash`:
+
+[source, ruby]
+------------------------------------------
+class ClientsController < ApplicationController
+
+ def create
+ @client = Client.new(params[:client])
+ if @client.save
+ # ...
+ else
+ flash.now[:error] = "Could not save client"
+ render :action => "new"
+ end
+ end
+
+end
+------------------------------------------
View
56 railties/doc/guides/actionview/layouts_and_rendering.txt
@@ -635,6 +635,17 @@ Here, the +_ad_banner.html.erb+ and +_footer.html.erb+ partials could contain co
TIP: For content that is shared among all pages in your application, you can use partials directly from layouts.
+==== Partial Layouts
+
+A partial can use its own layout file, just as a view can use a layout. For example, you might call a partial like this:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => "link_area", :layout => "graybar" %>
+-------------------------------------------------------
+
+This would look for a partial named +_link_area.html.erb+ and render it using the layout +_graybar.html.erb+. Note that layouts for partials follow the same leading-underscore naming as regular partials, and are placed in the same folder with the partial that they belong to (not in the master +layouts+ folder).
+
==== Passing Local Variables
You can also pass local variables into partials, making them even more powerful and flexible. For example, you can use this technique to reduce duplication between new and edit pages, while still keeping a bit of distinct content:
@@ -677,6 +688,15 @@ Every partial also has a local variable with the same name as the partial (minus
Within the +customer+ partial, the +@customer+ variable will refer to +@new_customer+ from the parent view.
+If you have an instance of a model to render into a partial, you can use a shorthand syntax:
+
+[source, html]
+-------------------------------------------------------
+<%= render :partial => @customer %>
+-------------------------------------------------------
+
+Assuming that the +@customer+ instance variable contains an instance of the +Customer+ model, this will use +_customer.html.erb+ to render it.
+
==== Rendering Collections
Partials are very useful in rendering collections. When you pass a collection to a partial via the +:collection+ option, the partial will be inserted once for each member in the collection:
@@ -711,10 +731,45 @@ You can also specify a second partial to be rendered between instances of the ma
Rails will render the +_product_ruler+ partial (with no data passed in to it) between each pair of +_product+ partials.
+There's also a shorthand syntax available for rendering collections. For example, if +@products+ is a collection of products, you can render the collection this way:
+
+[source, html]
+-------------------------------------------------------
+index.rhtml.erb:
+
+<h1>Products</h1>
+<%= render :partial => @products %>
+
+_product.html.erb:
+
+<p>Product Name: <%= product.name %></p>
+-------------------------------------------------------
+
+Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection:
+
+[source, html]
+-------------------------------------------------------
+index.rhtml.erb:
+
+<h1>Contacts</h1>
+<%= render :partial => [customer1, employee1, customer2, employee2] %>
+
+_customer.html.erb:
+
+<p>Name: <%= customer.name %></p>
+
+_employee.html.erb:
+
+<p>Name: <%= employee.name %></p>
+-------------------------------------------------------
+
+In this case, Rails will use the customer or employee partials as appropriate for each member of the collection.
+
== Changelog ==
http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/15[Lighthouse ticket]
+* October 16, 2008: Ready for publication by link:../authors.html#mgunderloy[Mike Gunderloy]
* October 4, 2008: Additional info on partials (+:object+, +:as+, and +:spacer_template+) by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
* September 28, 2008: First draft by link:../authors.html#mgunderloy[Mike Gunderloy] (not yet approved for publication)
@@ -757,4 +812,3 @@ http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/15[Lighthouse
-
View
2  railties/doc/guides/activerecord/active_record_basics.txt
@@ -0,0 +1,2 @@
+ActiveRecord Basics
+=================================
View
37 railties/doc/guides/activerecord/association_basics.txt
@@ -354,8 +354,8 @@ In designing a data model, you will sometimes find a model that should have a re
[source, ruby]
-------------------------------------------------------
class Employee < ActiveRecord::Base
- has_many :subordinates, :class_name => :user, :foreign_key => "manager_id"
- belongs_to :manager, :class_name => :user
+ has_many :subordinates, :class_name => "User", :foreign_key => "manager_id"
+ belongs_to :manager, :class_name => "User"
end
-------------------------------------------------------
@@ -636,12 +636,12 @@ The +belongs_to+ association supports these options:
//
===== +:class_name+
-If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if an order belongs to a customer, but the actual name of the model containing customers is patron, you'd set things up this way:
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if an order belongs to a customer, but the actual name of the model containing customers is +Patron+, you'd set things up this way:
[source, ruby]
-------------------------------------------------------
class Order < ActiveRecord::Base
- belongs_to :customer, :class_name => :patron
+ belongs_to :customer, :class_name => "Patron"
end
-------------------------------------------------------
@@ -711,7 +711,7 @@ By convention, Rails guesses that the column used to hold the foreign key on thi
[source, ruby]
-------------------------------------------------------
class Order < ActiveRecord::Base
- belongs_to :customer, :class_name => :patron, :foreign_key => "patron_id"
+ belongs_to :customer, :class_name => "Patron", :foreign_key => "patron_id"
end
-------------------------------------------------------
@@ -863,7 +863,7 @@ In many situations, you can use the default behavior of +has_one+ without any cu
[source, ruby]
-------------------------------------------------------
class Supplier < ActiveRecord::Base
- has_one :account, :class_name => :billing, :dependent => :nullify
+ has_one :account, :class_name => "Billing", :dependent => :nullify
end
-------------------------------------------------------
@@ -895,12 +895,12 @@ Setting the +:as+ option indicates that this is a polymorphic association. Polym
===== +:class_name+
-If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a supplier has an account, but the actual name of the model containing accounts is billing, you'd set things up this way:
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a supplier has an account, but the actual name of the model containing accounts is Billing, you'd set things up this way:
[source, ruby]
-------------------------------------------------------
class Supplier < ActiveRecord::Base
- has_one :account, :class_name => :billing
+ has_one :account, :class_name => "Billing"
end
-------------------------------------------------------
@@ -1085,7 +1085,8 @@ The +_collection_.delete+ method removes one or more objects from the collection
@customer.orders.delete(@order1)
-------------------------------------------------------
-WARNING: The +_collection_.delete+ method will destroy the deleted object if they are declared as +belongs_to+ and are dependent on this model.
+WARNING: Objects will be in addition destroyed if they're associated with +:dependent => :destroy+, and deleted if they're associated with +:dependent => :delete_all+.
+
===== +_collection_=objects+
@@ -1205,12 +1206,12 @@ Setting the +:as+ option indicates that this is a polymorphic association, as di
===== +:class_name+
-If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a customer has many orders, but the actual name of the model containing orders is transactions, you'd set things up this way:
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a customer has many orders, but the actual name of the model containing orders is +Transaction+, you'd set things up this way:
[source, ruby]
-------------------------------------------------------
class Customer < ActiveRecord::Base
- has_many :orders, :class_name => :transaction
+ has_many :orders, :class_name => "Transaction"
end
-------------------------------------------------------
@@ -1221,7 +1222,7 @@ The +:conditions+ option lets you specify the conditions that the associated obj
[source, ruby]
-------------------------------------------------------
class Customer < ActiveRecord::Base
- has_many :confirmed_orders, :class_name => :orders, :conditions => "confirmed = 1"
+ has_many :confirmed_orders, :class_name => "Order", :conditions => "confirmed = 1"
end
-------------------------------------------------------
@@ -1230,7 +1231,7 @@ You can also set conditions via a hash:
[source, ruby]
-------------------------------------------------------
class Customer < ActiveRecord::Base
- has_many :confirmed_orders, :class_name => :orders, :conditions => { :confirmed => true }
+ has_many :confirmed_orders, :class_name => "Order", :conditions => { :confirmed => true }
end
-------------------------------------------------------
@@ -1321,7 +1322,7 @@ The +:limit+ option lets you restrict the total number of objects that will be f
[source, ruby]
-------------------------------------------------------
class Customer < ActiveRecord::Base
- has_many :recent_orders, :class_name => :orders, :order => "order_date DESC", :limit => 100
+ has_many :recent_orders, :class_name => "Order", :order => "order_date DESC", :limit => 100
end
-------------------------------------------------------
@@ -1591,19 +1592,19 @@ TIP: The +:foreign_key+ and +:association_foreign_key+ options are useful when s
[source, ruby]
-------------------------------------------------------
class User < ActiveRecord::Base
- has_and_belongs_to_many :friends, :class_name => :users,
+ has_and_belongs_to_many :friends, :class_name => "User",
:foreign_key => "this_user_id", :association_foreign_key => "other_user_id"
end
-------------------------------------------------------
===== +:class_name+
-If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a part has many assemblies, but the actual name of the model containing assemblies is gadgets, you'd set things up this way:
+If the name of the other model cannot be derived from the association name, you can use the +:class_name+ option to supply the model name. For example, if a part has many assemblies, but the actual name of the model containing assemblies is +Gadget+, you'd set things up this way:
[source, ruby]
-------------------------------------------------------
class Parts < ActiveRecord::Base
- has_and_belongs_to_many :assemblies, :class_name => :gadgets
+ has_and_belongs_to_many :assemblies, :class_name => "Gadget"
end
-------------------------------------------------------
@@ -1654,7 +1655,7 @@ By convention, Rails guesses that the column in the join table used to hold the
[source, ruby]
-------------------------------------------------------
class User < ActiveRecord::Base
- has_and_belongs_to_many :friends, :class_name => :users,
+ has_and_belongs_to_many :friends, :class_name => "User",
:foreign_key => "this_user_id", :association_foreign_key => "other_user_id"
end
-------------------------------------------------------
View
9 railties/doc/guides/benchmarking_and_profiling/appendix.txt
@@ -90,15 +90,6 @@ service both for when in development and when you put your application into prod
#TODO more in-depth without being like an advertisement.
-=== FiveRuns ===
-http://www.fiveruns.com/[http://www.fiveruns.com/]
-
-#TODO give a bit more detail
-
-==== TuneUp ====
-
-In their words "a new socially networked application profiling tool for Ruby on Rails developers. Designed for rapid application performance analysis in development, both privately or collaboratively with input from the community, FiveRuns TuneUp gives developers visibility into performance trouble spots and bottlenecks before they reach production."
-
==== Manage ====
Like new relic a production monitoring tool.
View
55 railties/doc/guides/benchmarking_and_profiling/basics.txt
@@ -1,55 +0,0 @@
-== On The Road to Optimization ==
-=== Looking at the log file in regards to optimization ===
-
-You actually have been gathering data for benchmarking throughout your development cycle. Your log files are not just for error detection they also contain very useful information on how speedy your action is behaving.
-
-.Regular Log Output
-----------------------------------------------------------------------------
-
-Processing MediaController#index (for 127.0.0.1 at 2008-07-17 21:30:21) [GET]
-
- Session ID: BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo
-SGFzaHsABjoKQHVzZWR7AA==--cb57dad9c5e4704f0e1eddb3d498fef544faaf46
-
- Parameters: {"action"=>"index", "controller"=>"media"}
-
- Product Columns (0.003187) SHOW FIELDS FROM `products`