Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge with mainstream rails

  • Loading branch information...
commit 0242d817035e60357e559af0e9441a1f24a764e4 2 parents 7e27773 + 9cb5400
@lifo lifo authored
Showing with 819 additions and 150 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. +6 −12 activerecord/lib/active_record/associations.rb
  18. +4 −0 activerecord/lib/active_record/associations/association_collection.rb
  19. +11 −0 activerecord/lib/active_record/associations/association_proxy.rb
  20. +1 −1  activerecord/lib/active_record/associations/has_one_association.rb
  21. +15 −4 activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
  22. +10 −13 activerecord/test/cases/associations/belongs_to_associations_test.rb
  23. +41 −1 activerecord/test/cases/associations/eager_test.rb
  24. +4 −0 activerecord/test/cases/associations/has_many_associations_test.rb
  25. +10 −0 activerecord/test/cases/associations/has_one_associations_test.rb
  26. +10 −0 activerecord/test/cases/associations/has_one_through_associations_test.rb
  27. +0 −20 activerecord/test/cases/associations_test.rb
  28. +2 −0  activerecord/test/models/author.rb
  29. +6 −0 activerecord/test/models/club.rb
  30. +12 −1 activerecord/test/models/company.rb
  31. +1 −0  activerecord/test/models/post.rb
  32. +2 −0  activeresource/CHANGELOG
  33. +36 −0 activeresource/lib/active_resource/base.rb
  34. +1 −1  activeresource/lib/active_resource/formats/json_format.rb
  35. +17 −0 activeresource/test/format_test.rb
  36. +2 −0  activesupport/CHANGELOG
  37. +2 −1  activesupport/lib/active_support/core_ext/array/conversions.rb
  38. +2 −0  activesupport/lib/active_support/core_ext/float.rb
  39. +27 −0 activesupport/lib/active_support/core_ext/float/time.rb
  40. +2 −0  activesupport/lib/active_support/core_ext/integer.rb
  41. +45 −0 activesupport/lib/active_support/core_ext/integer/time.rb
  42. +0 −10 activesupport/lib/active_support/core_ext/numeric/time.rb
  43. +10 −0 activesupport/lib/active_support/core_ext/time/calculations.rb
  44. +26 −6 activesupport/lib/active_support/inflector.rb
  45. +1 −0  activesupport/lib/active_support/locale/en-US.yml
  46. +7 −3 activesupport/lib/active_support/memoizable.rb
  47. +2 −2 activesupport/lib/active_support/multibyte.rb
  48. +14 −2 activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
  49. +50 −0 activesupport/test/core_ext/duration_test.rb
  50. +8 −0 activesupport/test/core_ext/time_ext_test.rb
  51. +16 −0 activesupport/test/i18n_test.rb
  52. +2 −0  activesupport/test/inflector_test_cases.rb
  53. +12 −0 activesupport/test/json/encoding_test.rb
  54. +5 −0 activesupport/test/memoizable_test.rb
  55. +1 −1  railties/doc/guides/securing_rails_applications/security.txt
  56. +1 −1  railties/environments/environment.rb
  57. +2 −2 railties/lib/console_sandbox.rb
  58. +35 −4 railties/lib/rails/gem_dependency.rb
  59. +1 −1  railties/lib/rails/mongrel_server/commands.rb
  60. +68 −21 railties/lib/rails/vendor_gem_source_index.rb
  61. +1 −0  railties/lib/rails_generator/commands.rb
  62. +1 −1  railties/lib/rails_generator/generators/components/controller/controller_generator.rb
  63. +1 −1  railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb
  64. +1 −1  railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb
  65. +1 −1  railties/lib/rails_generator/generators/components/model/model_generator.rb
  66. +1 −1  railties/lib/rails_generator/generators/components/observer/observer_generator.rb
  67. +1 −1  railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
  68. +1 −1  railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
  69. +2 −2 railties/lib/rails_generator/generators/components/resource/resource_generator.rb
  70. +2 −2 railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
  71. +10 −0 railties/lib/tasks/gems.rake
  72. +20 −0 railties/test/gem_dependency_test.rb
  73. +3 −0  railties/test/generators/rails_controller_generator_test.rb
  74. +1 −0  railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb
  75. +28 −0 railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification
  76. +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
18 activerecord/lib/active_record/associations.rb
@@ -1235,7 +1235,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 +1248,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 +1269,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
4 activerecord/lib/active_record/associations/association_collection.rb
@@ -320,6 +320,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
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
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/doc/guides/securing_rails_applications/security.txt
@@ -859,4 +859,4 @@ The security landscape shifts and it is important to keep up to date, because mi
- Subscribe to the Rails security http://groups.google.com/group/rubyonrails-security[mailing list]
- http://secunia.com/[Keep up to date on the other application layers] (they have a weekly newsletter, too)
- A http://ha.ckers.org/blog/[good security blog] including the http://ha.ckers.org/xss.html[Cross-Site scripting Cheat Sheet]
-- Another http://www.0x000000.com/[good security blog] with some Cheat Sheets, too
+- Another http://www.0x000000.com/[good security blog] with some Cheat Sheets, too
View
2  railties/environments/environment.rb
@@ -22,7 +22,7 @@
# Specify gems that this application depends on.
# They can then be installed with "rake gems:install" on new installations.
- # You HAVE to specify the <tt>:lib</tt> option for libraries, where the Gem name (<em>sqlite3-ruby</em>) differs from the file itself (_sqlite3_)
+ # You have to specify the <tt>:lib</tt> option for libraries, where the Gem name (<em>sqlite3-ruby</em>) differs from the file itself (_sqlite3_)
# config.gem "bj"
# config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
# config.gem "sqlite3-ruby", :lib => "sqlite3"
View
4 railties/lib/console_sandbox.rb
@@ -1,6 +1,6 @@
-ActiveRecord::Base.send :increment_open_transactions
+ActiveRecord::Base.connection.increment_open_transactions
ActiveRecord::Base.connection.begin_db_transaction
at_exit do
ActiveRecord::Base.connection.rollback_db_transaction
- ActiveRecord::Base.send :decrement_open_transactions
+ ActiveRecord::Base.connection.decrement_open_transactions
end
View
39 railties/lib/rails/gem_dependency.rb
@@ -82,6 +82,10 @@ def gem_dir(base_directory)
File.join(base_directory, specification.full_name)
end
+ def spec_filename(base_directory)
+ File.join(gem_dir(base_directory), '.specification')
+ end
+
def load
return if @loaded || @load_paths_added == false
require(@lib || name) unless @lib == false
@@ -108,6 +112,8 @@ def loaded?
@loaded ||= begin
if vendor_rails?
true
+ elsif specification.nil?
+ false
else
# check if the gem is loaded by inspecting $"
# specification.files lists all the files contained in the gem
@@ -144,17 +150,42 @@ def unpack_to(directory)
Gem::GemRunner.new.run(unpack_command)
end
+ # Gem.activate changes the spec - get the original
+ real_spec = Gem::Specification.load(spec.loaded_from)
+ write_spec(directory, real_spec)
+
+ end
+
+ def write_spec(directory, spec)
# copy the gem's specification into GEMDIR/.specification so that
# we can access information about the gem on deployment systems
# without having the gem installed
- spec_filename = File.join(gem_dir(directory), '.specification')
- # Gem.activate changes the spec - get the original
- spec = Gem::Specification.load(specification.loaded_from)
- File.open(spec_filename, 'w') do |file|
+ File.open(spec_filename(directory), 'w') do |file|
file.puts spec.to_yaml
end
end
+ def refresh_spec(directory)
+ real_gems = Gem.source_index.installed_source_index
+ exact_dep = Gem::Dependency.new(name, "= #{specification.version}")
+ matches = real_gems.search(exact_dep)
+ installed_spec = matches.first
+ if installed_spec
+ # we have a real copy
+ # get a fresh spec - matches should only have one element
+ # note that there is no reliable method to check that the loaded
+ # spec is the same as the copy from real_gems - Gem.activate changes
+ # some of the fields
+ real_spec = Gem::Specification.load(matches.first.loaded_from)
+ write_spec(directory, real_spec)
+ puts "Reloaded specification for #{name} from installed gems."
+ else
+ # the gem isn't installed locally - write out our current specs
+ write_spec(directory, specification)
+ puts "Gem #{name} not loaded locally - writing out current spec."
+ end
+ end
+
def ==(other)
self.name == other.name && self.requirement == other.requirement
end
View
2  railties/lib/rails/mongrel_server/commands.rb
@@ -44,7 +44,7 @@ def mount_rails(prefix)
env_location = "#{defaults[:cwd]}/config/environment"
require env_location
- ActionController::AbstractRequest.relative_url_root = defaults[:prefix]
+ ActionController::Base.relative_url_root = defaults[:prefix]
uri prefix, :handler => Rails::MongrelServer::RailsHandler.new
end
end
View
89 railties/lib/rails/vendor_gem_source_index.rb
@@ -13,6 +13,16 @@ class VendorGemSourceIndex
attr_reader :installed_source_index
attr_reader :vendor_source_index
+ @@silence_spec_warnings = false
+
+ def self.silence_spec_warnings
+ @@silence_spec_warnings
+ end
+
+ def self.silence_spec_warnings=(v)
+ @@silence_spec_warnings = v
+ end
+
def initialize(installed_index, vendor_dir=Rails::GemDependency.unpacked_path)
@installed_source_index = installed_index
@vendor_dir = vendor_dir
@@ -33,38 +43,75 @@ def refresh!
# load specifications from vendor/gems
Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |d|
+ dir_name = File.basename(d)
+ dir_version = version_for_dir(dir_name)
spec = load_specification(d)
- next unless spec
- # NOTE: this is a bit of a hack - the gem system expects a different structure
- # than we have.
- # It's looking for:
- # repository
- # -> specifications
- # - gem_name.spec <= loaded_from points to this
- # -> gems
- # - gem_name <= gem files here
- # and therefore goes up one directory from loaded_from, then adds gems/gem_name
- # to the path.
- # But we have:
- # vendor
- # -> gems
- # -> gem_name <= gem files here
- # - .specification
- # so we set loaded_from to vendor/gems/.specification (not a real file) to
- # get the correct behavior.
- spec.loaded_from = File.join(Rails::GemDependency.unpacked_path, '.specification')
+ if spec
+ if spec.full_name != dir_name
+ # mismatched directory name and gem spec - produced by 2.1.0-era unpack code
+ if dir_version
+ # fix the spec version - this is not optimal (spec.files may be wrong)
+ # but it's better than breaking apps. Complain to remind users to get correct specs.
+ # use ActiveSupport::Deprecation.warn, as the logger is not set yet
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has a mismatched specification file."+
+ " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings
+ spec.version = dir_version
+ else
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems is not in a versioned directory"+
+ "(should be #{spec.full_name}).") unless @@silence_spec_warnings
+ # continue, assume everything is OK
+ end
+ end
+ else
+ # no spec - produced by early-2008 unpack code
+ # emulate old behavior, and complain.
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has no specification file."+
+ " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings
+ if dir_version
+ spec = Gem::Specification.new
+ spec.version = dir_version
+ spec.require_paths = ['lib']
+ ext_path = File.join(d, 'ext')
+ spec.require_paths << 'ext' if File.exist?(ext_path)
+ spec.name = /^(.*)-[^-]+$/.match(dir_name)[1]
+ files = ['lib']
+ # set files to everything in lib/
+ files += Dir[File.join(d, 'lib', '*')].map { |v| v.gsub(/^#{d}\//, '') }
+ files += Dir[File.join(d, 'ext', '*')].map { |v| v.gsub(/^#{d}\//, '') } if ext_path
+ spec.files = files
+ else
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems not in a versioned directory."+
+ " Giving up.") unless @silence_spec_warnings
+ next
+ end
+ end
+ spec.loaded_from = File.join(d, '.specification')
+ # finally, swap out full_gem_path
+ # it would be better to use a Gem::Specification subclass, but the YAML loads an explicit class
+ class << spec
+ def full_gem_path
+ path = File.join installation_path, full_name
+ return path if File.directory? path
+ File.join installation_path, original_name
+ end
+ end
vendor_gems[File.basename(d)] = spec
end
@vendor_source_index = Gem::SourceIndex.new(vendor_gems)
end
+ def version_for_dir(d)
+ matches = /-([^-]+)$/.match(d)
+ Gem::Version.new(matches[1]) if matches
+ end
+
def load_specification(gem_dir)
spec_file = File.join(gem_dir, '.specification')
YAML.load_file(spec_file) if File.exist?(spec_file)
end
- def find_name(gem_name, version_requirement = Gem::Requirement.default)
- search(/^#{gem_name}$/, version_requirement)
+ def find_name(*args)
+ @installed_source_index.find_name(*args) + @vendor_source_index.find_name(*args)
end
def search(*args)
View
1  railties/lib/rails_generator/commands.rb
@@ -169,6 +169,7 @@ class Create < Base
# Ruby or Rails. In the future, expand to check other namespaces
# such as the rest of the user's app.
def class_collisions(*class_names)
+ path = class_names.shift
class_names.flatten.each do |class_name|
# Convert to string to allow symbol arguments.
class_name = class_name.to_s
View
2  railties/lib/rails_generator/generators/components/controller/controller_generator.rb
@@ -2,7 +2,7 @@ class ControllerGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions class_path, "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper"
+ m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper"
# Controller, helper, views, and test directories.
m.directory File.join('app/controllers', class_path)
View
2  railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb
@@ -4,7 +4,7 @@ class IntegrationTestGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions class_path, class_name, "#{class_name}Test"
+ m.class_collisions class_name, "#{class_name}Test"
# integration test directory
m.directory File.join('test/integration', class_path)
View
2  railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb
@@ -2,7 +2,7 @@ class MailerGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions class_path, class_name, "#{class_name}Test"
+ m.class_collisions class_name, "#{class_name}Test"
# Mailer, view, test, and fixture directories.
m.directory File.join('app/models', class_path)
View
2  railties/lib/rails_generator/generators/components/model/model_generator.rb
@@ -4,7 +4,7 @@ class ModelGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions class_path, class_name, "#{class_name}Test"
+ m.class_collisions class_name, "#{class_name}Test"
# Model, test, and fixture directories.
m.directory File.join('app/models', class_path)
View
2  railties/lib/rails_generator/generators/components/observer/observer_generator.rb
@@ -2,7 +2,7 @@ class ObserverGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions class_path, "#{class_name}Observer", "#{class_name}ObserverTest"
+ m.class_collisions "#{class_name}Observer", "#{class_name}ObserverTest"
# Observer, and test directories.
m.directory File.join('app/models', class_path)
View
2  railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
@@ -4,7 +4,7 @@ class PerformanceTestGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions class_path, class_name, "#{class_name}Test"
+ m.class_collisions class_name, "#{class_name}Test"
# performance test directory
m.directory File.join('test/performance', class_path)
View
2  railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
@@ -10,7 +10,7 @@ def initialize(runtime_args, runtime_options = {})
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions class_path, class_name
+ m.class_collisions class_name
m.directory "#{plugin_path}/lib"
m.directory "#{plugin_path}/tasks"
View
4 railties/lib/rails_generator/generators/components/resource/resource_generator.rb
@@ -30,8 +30,8 @@ def initialize(runtime_args, runtime_options = {})
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
- m.class_collisions(class_path, "#{class_name}")
+ m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
+ m.class_collisions(class_name)
# Controller, helper, views, and test directories.
m.directory(File.join('app/models', class_path))
View
4 railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
@@ -36,8 +36,8 @@ def initialize(runtime_args, runtime_options = {})
def manifest
record do |m|
# Check for class naming collisions.
- m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
- m.class_collisions(class_path, "#{class_name}")
+ m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
+ m.class_collisions(class_name)
# Controller, helper, views, test and stylesheets directories.
m.directory(File.join('app/models', class_path))
View
10 railties/lib/tasks/gems.rake
@@ -65,4 +65,14 @@ namespace :gems do
end
end
end
+
+ desc "Regenerate gem specifications in correct format."
+ task :refresh_specs => :base do
+ require 'rubygems'
+ require 'rubygems/gem_runner'
+ Rails.configuration.gems.each do |gem|
+ next unless gem.frozen? && (ENV['GEM'].blank? || ENV['GEM'] == gem.name)
+ gem.refresh_spec(Rails::GemDependency.unpacked_path) if gem.loaded?
+ end
+ end
end
View
20 railties/test/gem_dependency_test.rb
@@ -1,3 +1,6 @@
+require 'lib/rails/vendor_gem_source_index'
+Rails::VendorGemSourceIndex.silence_spec_warnings = true
+
require 'plugin_test_helper'
class Rails::GemDependency
@@ -110,5 +113,22 @@ def test_gem_load_frozen_minimum_version
assert_equal '0.6.0', DUMMY_GEM_C_VERSION
end
+ def test_gem_load_missing_specification
+ dummy_gem = Rails::GemDependency.new "dummy-gem-d"
+ dummy_gem.add_load_paths
+ dummy_gem.load
+ assert_not_nil DUMMY_GEM_D_VERSION
+ assert_equal '1.0.0', DUMMY_GEM_D_VERSION
+ assert_equal ['lib', 'lib/dummy-gem-d.rb'], dummy_gem.specification.files
+ end
+
+ def test_gem_load_bad_specification
+ dummy_gem = Rails::GemDependency.new "dummy-gem-e", :version => "= 1.0.0"
+ dummy_gem.add_load_paths
+ dummy_gem.load
+ assert_not_nil DUMMY_GEM_E_VERSION
+ assert_equal '1.0.0', DUMMY_GEM_E_VERSION
+ end
+
end
end
View
3  railties/test/generators/rails_controller_generator_test.rb
@@ -1,5 +1,8 @@
require 'generators/generator_test_helper'
+module Admin
+end
+
class RailsControllerGeneratorTest < GeneratorTestCase
def test_controller_generates_controller
View
1  railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb
@@ -0,0 +1 @@
+DUMMY_GEM_D_VERSION="1.0.0"
View
28 railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification
@@ -0,0 +1,28 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-e
+version: !ruby/object:Gem::Version
+ version: 1.3.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+files:
+- lib
+- lib/dummy-gem-e.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem E
View
1  railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb
@@ -0,0 +1 @@
+DUMMY_GEM_E_VERSION="1.0.0"
Please sign in to comment.