aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-11-02 03:52:15 +0530
committerPratik Naik <pratiknaik@gmail.com>2008-11-02 03:52:15 +0530
commit1147453fce0890ea229c3af5f43c909ebe53061e (patch)
tree221d816ef0c908044fd6029950ccad064866ab8f
parenta3aa0c17ef8594a0084511f4852be7b5dc66e5e2 (diff)
parent5a02f0bccf55191c2cfbcc69bd8165df6d7a2012 (diff)
downloadrails-1147453fce0890ea229c3af5f43c909ebe53061e.tar.gz
rails-1147453fce0890ea229c3af5f43c909ebe53061e.tar.bz2
rails-1147453fce0890ea229c3af5f43c909ebe53061e.zip
Merge commit 'mainstream/master'
Conflicts: railties/doc/guides/html/layouts_and_rendering.html railties/doc/guides/source/active_record_basics.txt railties/doc/guides/source/layouts_and_rendering.txt
-rw-r--r--actionpack/CHANGELOG11
-rw-r--r--actionpack/lib/action_controller/base.rb59
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb4
-rw-r--r--actionpack/lib/action_controller/routing/optimisations.rb26
-rw-r--r--actionpack/lib/action_controller/routing/recognition_optimisation.rb8
-rw-r--r--actionpack/lib/action_controller/test_process.rb2
-rw-r--r--actionpack/lib/action_view/base.rb11
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb17
-rw-r--r--actionpack/lib/action_view/partials.rb2
-rw-r--r--actionpack/lib/action_view/renderable.rb14
-rw-r--r--actionpack/test/controller/redirect_test.rb10
-rw-r--r--actionpack/test/controller/render_test.rb20
-rw-r--r--actionpack/test/fixtures/layouts/_column.html.erb2
-rw-r--r--actionpack/test/fixtures/test/_customer.erb2
-rw-r--r--actionpack/test/fixtures/test/nested_layout.erb3
-rw-r--r--actionpack/test/fixtures/test/template.erb1
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb8
-rw-r--r--actionpack/test/template/render_test.rb18
-rw-r--r--actionpack/test/template/translation_helper_test.rb4
-rw-r--r--actionpack/test/template/url_helper_test.rb23
-rw-r--r--activerecord/CHANGELOG7
-rwxr-xr-xactiverecord/lib/active_record/associations.rb21
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rwxr-xr-xactiverecord/lib/active_record/base.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb147
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb18
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb11
-rwxr-xr-xactiverecord/test/cases/base_test.rb6
-rw-r--r--activerecord/test/cases/finder_test.rb11
-rw-r--r--activeresource/CHANGELOG5
-rw-r--r--activeresource/lib/active_resource/custom_methods.rb2
-rw-r--r--activeresource/test/base/custom_methods_test.rb2
-rw-r--r--activesupport/CHANGELOG9
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/deprecation.rb4
-rw-r--r--activesupport/lib/active_support/string_inquirer.rb2
-rwxr-xr-xactivesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb15
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb36
-rw-r--r--railties/CHANGELOG7
-rw-r--r--railties/Rakefile2
-rw-r--r--railties/doc/guides/source/active_record_basics.txt2
-rw-r--r--railties/html/javascripts/prototype.js337
-rw-r--r--railties/lib/initializer.rb1
-rw-r--r--railties/lib/rails/gem_dependency.rb37
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb20
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/templates/Rakefile1
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb3
-rw-r--r--railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb4
-rw-r--r--railties/lib/tasks/gems.rake3
-rw-r--r--railties/test/gem_dependency_test.rb5
53 files changed, 697 insertions, 338 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 3c06c87bb2..17fada156d 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,3 +1,14 @@
+*2.2.1 [RC2 or 2.2 final]*
+
+* Simplified the logging format for parameters (don't include controller, action, and format as duplicates) [DHH]
+
+* Remove the logging of the Session ID when the session store is CookieStore [DHH]
+
+* Fixed regex in redirect_to to fully support URI schemes #1247 [Seth Fitzsimmons]
+
+* Fixed bug with asset timestamping when using relative_url_root #1265 [Joe Goldwasser]
+
+
*2.2.0 [RC1] (October 24th, 2008)*
* Fix incorrect closing CDATA delimiter and that HTML::Node.parse would blow up on unclosed CDATA sections [packagethief]
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 006a3039af..cf86c5eed0 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -801,6 +801,19 @@ module ActionController #:nodoc:
# # Renders "Hello from code!"
# render :text => proc { |response, output| output.write("Hello from code!") }
#
+ # === Rendering XML
+ #
+ # Rendering XML sets the content type to application/xml.
+ #
+ # # Renders '<name>David</name>'
+ # render :xml => {:name => "David"}.to_xml
+ #
+ # It's not necessary to call <tt>to_xml</tt> on the object you want to render, since <tt>render</tt> will
+ # automatically do that for you:
+ #
+ # # Also renders '<name>David</name>'
+ # render :xml => {:name => "David"}
+ #
# === Rendering JSON
#
# Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
@@ -846,8 +859,14 @@ module ActionController #:nodoc:
# page.visual_effect :highlight, 'user_list'
# end
#
- # === Rendering with status and location headers
+ # === Rendering vanilla JavaScript
+ #
+ # In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js.
+ #
+ # # Renders "alert('hello')" and sets the mime type to text/javascript
+ # render :js => "alert('hello')"
#
+ # === Rendering with status and location headers
# All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together:
#
# render :xml => post.to_xml, :status => :created, :location => post_url(post)
@@ -898,6 +917,10 @@ module ActionController #:nodoc:
response.content_type ||= Mime::XML
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
+ elsif js = options[:js]
+ response.content_type ||= Mime::JS
+ render_for_text(js, options[:status])
+
elsif json = options[:json]
json = json.to_json unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
@@ -933,6 +956,7 @@ module ActionController #:nodoc:
def render_to_string(options = nil, &block) #:doc:
render(options, &block)
ensure
+ response.content_type = nil
erase_render_results
reset_variables_added_to_assigns
end
@@ -1056,7 +1080,10 @@ module ActionController #:nodoc:
logger.info("Redirected to #{options}") if logger && logger.info?
case options
- when %r{^\w+://.*}
+ # The scheme name consist of a letter followed by any combination of
+ # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
+ # characters; and is terminated by a colon (":").
+ when %r{^\w[\w\d+.-]*:.*}
redirect_to_full_url(options, status)
when String
redirect_to_full_url(request.protocol + request.host_with_port + options, status)
@@ -1201,11 +1228,33 @@ module ActionController #:nodoc:
def log_processing
if logger && logger.info?
- logger.info "\n\nProcessing #{self.class.name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
- logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
- logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
+ log_processing_for_request_id
+ log_processing_for_session_id
+ log_processing_for_parameters
end
end
+
+ def log_processing_for_request_id
+ request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
+ request_id << "to #{params[:format]} " if params[:format]
+ request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]"
+
+ logger.info(request_id)
+ end
+
+ def log_processing_for_session_id
+ if @_session && @_session.respond_to?(:session_id) && @_session.respond_to?(:dbman) &&
+ !@_session.dbman.is_a?(CGI::Session::CookieStore)
+ logger.info " Session ID: #{@_session.session_id}"
+ end
+ end
+
+ def log_processing_for_parameters
+ parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params
+ parameters = parameters.except(:controller, :action, :format)
+
+ logger.info " Parameters: #{parameters.inspect}"
+ end
def default_render #:nodoc:
render
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index 28f8ce3d53..f3e173004a 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -28,6 +28,10 @@ module ActionController
end
after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush)
+
+ to_prepare do
+ I18n.reload!
+ end
end
# Backward-compatible class method takes CGI-specific args. Deprecated
diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb
index 894d4109e4..0a87303bda 100644
--- a/actionpack/lib/action_controller/routing/optimisations.rb
+++ b/actionpack/lib/action_controller/routing/optimisations.rb
@@ -20,14 +20,20 @@ module ActionController
class Optimiser
attr_reader :route, :kind
+ GLOBAL_GUARD_CONDITIONS = [
+ "(!defined?(default_url_options) || default_url_options.blank?)",
+ "(!defined?(controller.default_url_options) || controller.default_url_options.blank?)",
+ "defined?(request)",
+ "request"
+ ]
def initialize(route, kind)
@route = route
@kind = kind
end
- def guard_condition
- 'false'
+ def guard_conditions
+ ["false"]
end
def generation_code
@@ -36,6 +42,7 @@ module ActionController
def source_code
if applicable?
+ guard_condition = (GLOBAL_GUARD_CONDITIONS + guard_conditions).join(" && ")
"return #{generation_code} if #{guard_condition}\n"
else
"\n"
@@ -57,14 +64,14 @@ module ActionController
# return a string like "/people/#{@person.to_param}"
# rather than triggering the expensive logic in +url_for+.
class PositionalArguments < Optimiser
- def guard_condition
+ def guard_conditions
number_of_arguments = route.segment_keys.size
# if they're using foo_url(:id=>2) it's one
# argument, but we don't want to generate /foos/id2
if number_of_arguments == 1
- "(!defined?(default_url_options) || default_url_options.blank?) && defined?(request) && request && args.size == 1 && !args.first.is_a?(Hash)"
+ ["args.size == 1", "!args.first.is_a?(Hash)"]
else
- "(!defined?(default_url_options) || default_url_options.blank?) && defined?(request) && request && args.size == #{number_of_arguments}"
+ ["args.size == #{number_of_arguments}"]
end
end
@@ -98,8 +105,13 @@ module ActionController
# above, but it supports additional query parameters as the last
# argument
class PositionalArgumentsWithAdditionalParams < PositionalArguments
- def guard_condition
- "(!defined?(default_url_options) || default_url_options.blank?) && defined?(request) && request && args.size == #{route.segment_keys.size + 1} && !args.last.has_key?(:anchor) && !args.last.has_key?(:port) && !args.last.has_key?(:host)"
+ def guard_conditions
+ [
+ "args.size == #{route.segment_keys.size + 1}",
+ "!args.last.has_key?(:anchor)",
+ "!args.last.has_key?(:port)",
+ "!args.last.has_key?(:host)"
+ ]
end
# This case uses almost the same code as positional arguments,
diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
index 4935432d87..6c47ced6d1 100644
--- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb
+++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
@@ -153,13 +153,7 @@ module ActionController
def clear_recognize_optimized!
remove_recognize_optimized!
-
- class << self
- def recognize_optimized(path, environment)
- write_recognize_optimized!
- recognize_optimized(path, environment)
- end
- end
+ write_recognize_optimized!
end
def remove_recognize_optimized!
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index f84c48f102..7a31f0e8d5 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -218,7 +218,7 @@ module ActionController #:nodoc:
# Returns the template of the file which was used to
# render this response (or nil)
def rendered_template
- template.send(:_first_render)
+ template.instance_variable_get(:@_first_render)
end
# A shortcut to the flash. Returns an empty hash if no session flash exists.
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index e22978fe27..945246a39a 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -222,6 +222,7 @@ module ActionView #:nodoc:
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
@assigns = assigns_for_first_render
@assigns_added = nil
+ @_render_stack = []
@controller = controller
@helpers = ProxyModule.new(self)
self.view_paths = view_paths
@@ -271,9 +272,13 @@ module ActionView #:nodoc:
end
end
- private
- attr_accessor :_first_render, :_last_render
+ # Access the current template being rendered.
+ # Returns a ActionView::Template object.
+ def template
+ @_render_stack.last
+ end
+ private
# Evaluates the local assigns and controller ivars, pushes them to the view.
def _evaluate_assigns_and_ivars #:nodoc:
unless @assigns_added
@@ -312,7 +317,7 @@ module ActionView #:nodoc:
template
elsif template = self.view_paths[template_file_name]
template
- elsif _first_render && template = self.view_paths["#{template_file_name}.#{_first_render.format_and_extension}"]
+ elsif @_render_stack.first && template = self.view_paths["#{template_file_name}.#{@_render_stack.first.format_and_extension}"]
template
elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"]
@template_format = :html
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 93d38eb929..8bbe74b7ef 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -588,8 +588,8 @@ module ActionView
source += ".#{extension}" if missing_extension?(source)
unless source =~ ProtocolRegexp
source = "/#{directory}/#{source}" unless source[0] == ?/
- source = prepend_relative_url_root(source)
source = rewrite_asset_path(source)
+ source = prepend_relative_url_root(source)
end
source = prepend_asset_host(source)
source
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index de4c1d7689..dc41ef5305 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -3,12 +3,11 @@ require 'action_view/helpers/tag_helper'
module ActionView
module Helpers
module TranslationHelper
- def translate(*args)
- args << args.extract_options!.merge(:raise => true)
- I18n.translate *args
-
+ def translate(key, options = {})
+ options[:raise] = true
+ I18n.translate(key, options)
rescue I18n::MissingTranslationData => e
- keys = I18n.send :normalize_translation_keys, e.locale, e.key, e.options[:scope]
+ keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
content_tag('span', keys.join(', '), :class => 'translation_missing')
end
alias :t :translate
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 7ba42a3b72..2e0eb8766b 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -499,7 +499,7 @@ module ActionView
# True if the current request URI was generated by the given +options+.
#
# ==== Examples
- # Let's say we're in the <tt>/shop/checkout</tt> action.
+ # Let's say we're in the <tt>/shop/checkout?order=desc</tt> action.
#
# current_page?(:action => 'process')
# # => false
@@ -507,6 +507,9 @@ module ActionView
# current_page?(:controller => 'shop', :action => 'checkout')
# # => true
#
+ # current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc)
+ # # => false
+ #
# current_page?(:action => 'checkout')
# # => true
#
@@ -515,10 +518,18 @@ module ActionView
def current_page?(options)
url_string = CGI.escapeHTML(url_for(options))
request = @controller.request
+ # We ignore any extra parameters in the request_uri if the
+ # submitted url doesn't have any either. This lets the function
+ # work with things like ?order=asc
+ if url_string.index("?")
+ request_uri = request.request_uri
+ else
+ request_uri = request.request_uri.split('?').first
+ end
if url_string =~ /^\w+:\/\//
- url_string == "#{request.protocol}#{request.host_with_port}#{request.request_uri}"
+ url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
else
- url_string == request.request_uri
+ url_string == request_uri
end
end
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index 373bb92dc4..8841099900 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -181,7 +181,7 @@ module ActionView
ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
template = _pick_partial_template(_partial_path)
local_assigns[template.counter_name] = index
- result = template.render_partial(self, object, local_assigns, as)
+ result = template.render_partial(self, object, local_assigns.dup, as)
index += 1
result
end.join(spacer)
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index 0134bc988f..c23b8cde89 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -25,20 +25,26 @@ module ActionView
def render(view, local_assigns = {})
compile(local_assigns)
- view.send(:_first_render=, self) unless view.send(:_first_render)
- view.send(:_last_render=, self)
+ stack = view.instance_variable_get(:@_render_stack)
+ stack.push(self)
+
+ # This is only used for TestResponse to set rendered_template
+ view.instance_variable_set(:@_first_render, self) unless view.instance_variable_get(:@_first_render)
view.send(:_evaluate_assigns_and_ivars)
view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
- view.send(method_name(local_assigns), local_assigns) do |*names|
+ result = view.send(method_name(local_assigns), local_assigns) do |*names|
ivar = :@_proc_for_layout
- if view.instance_variable_defined?(ivar) and proc = view.instance_variable_get(ivar)
+ if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
view.capture(*names, &proc)
elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
view.instance_variable_get(ivar)
end
end
+
+ stack.pop
+ result
end
def method_name(local_assigns)
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 2f8bf7b6ee..c55307d645 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -73,6 +73,10 @@ class RedirectController < ActionController::Base
redirect_to "http://dev.rubyonrails.org/query?status=new"
end
+ def redirect_to_url_with_complex_scheme
+ redirect_to "x-test+scheme.complex:redirect"
+ end
+
def redirect_to_back
redirect_to :back
end
@@ -198,6 +202,12 @@ class RedirectTest < Test::Unit::TestCase
assert_redirected_to "http://dev.rubyonrails.org/query?status=new"
end
+ def test_redirect_to_url_with_complex_scheme
+ get :redirect_to_url_with_complex_scheme
+ assert_response :redirect
+ assert_equal "x-test+scheme.complex:redirect", redirect_to_url
+ end
+
def test_redirect_to_back
@request.env["HTTP_REFERER"] = "http://www.example.com/coming/from"
get :redirect_to_back
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index db2d5d885b..df9376727f 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -154,6 +154,10 @@ class TestController < ActionController::Base
render :json => {:hello => 'world'}.to_json
end
+ def render_json_with_render_to_string
+ render :json => {:hello => render_to_string(:partial => 'partial')}
+ end
+
def render_custom_code
render :text => "hello world", :status => 404
end
@@ -180,6 +184,10 @@ class TestController < ActionController::Base
render("test/hello")
end
+ def render_vanilla_js_hello
+ render :js => "alert('hello')"
+ end
+
def render_xml_hello
@name = "David"
render :template => "test/hello"
@@ -772,6 +780,12 @@ class RenderTest < Test::Unit::TestCase
assert_equal 'application/json', @response.content_type
end
+ def test_render_json_with_render_to_string
+ get :render_json_with_render_to_string
+ assert_equal '{"hello": "partial html"}', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+
def test_render_custom_code
get :render_custom_code
assert_response 404
@@ -834,6 +848,12 @@ class RenderTest < Test::Unit::TestCase
assert_equal "test", @response.body # name is explicitly set to 'test' inside the controller.
end
+ def test_render_vanilla_js
+ get :render_vanilla_js_hello
+ assert_equal "alert('hello')", @response.body
+ assert_equal "text/javascript", @response.content_type
+ end
+
def test_render_xml
get :render_xml_hello
assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body
diff --git a/actionpack/test/fixtures/layouts/_column.html.erb b/actionpack/test/fixtures/layouts/_column.html.erb
new file mode 100644
index 0000000000..96db002b8a
--- /dev/null
+++ b/actionpack/test/fixtures/layouts/_column.html.erb
@@ -0,0 +1,2 @@
+<div id="column"><%= yield :column %></div>
+<div id="content"><%= yield %></div> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_customer.erb b/actionpack/test/fixtures/test/_customer.erb
index 872d8c44e6..d8220afeda 100644
--- a/actionpack/test/fixtures/test/_customer.erb
+++ b/actionpack/test/fixtures/test/_customer.erb
@@ -1 +1 @@
-Hello: <%= customer.name %> \ No newline at end of file
+Hello: <%= customer.name rescue "Anonymous" %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/nested_layout.erb b/actionpack/test/fixtures/test/nested_layout.erb
new file mode 100644
index 0000000000..7b6dcbb6c7
--- /dev/null
+++ b/actionpack/test/fixtures/test/nested_layout.erb
@@ -0,0 +1,3 @@
+<% content_for :title, "title" -%>
+<% content_for :column do -%>column<% end -%>
+<% render :layout => 'layouts/column' do -%>content<% end -%> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/template.erb b/actionpack/test/fixtures/test/template.erb
new file mode 100644
index 0000000000..785afa8f6a
--- /dev/null
+++ b/actionpack/test/fixtures/test/template.erb
@@ -0,0 +1 @@
+<%= template.path %> \ No newline at end of file
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 6dc1225035..bade96fe17 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -248,6 +248,14 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
end
+ def test_timebased_asset_id_with_relative_url_root
+ ActionController::Base.relative_url_root = "/collaboration/hieraki"
+ 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="#{ActionController::Base.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
+ ensure
+ ActionController::Base.relative_url_root = ""
+ end
+
def test_should_skip_asset_id_on_complete_url
assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png")
end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index a4ea22ddcb..476e651757 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -41,6 +41,10 @@ class ViewRenderTest < Test::Unit::TestCase
assert_equal "The secret is in the sauce\n", @view.render("test/dot.directory/render_file_with_ivar")
end
+ def test_render_has_access_current_template
+ assert_equal "test/template.erb", @view.render("test/template.erb")
+ end
+
def test_render_update
# TODO: You should not have to stub out template because template is self!
@view.instance_variable_set(:@template, @view)
@@ -111,6 +115,10 @@ class ViewRenderTest < Test::Unit::TestCase
assert_nil @view.render(:partial => "test/customer", :collection => nil)
end
+ def test_render_partial_with_nil_values_in_collection
+ assert_equal "Hello: davidHello: Anonymous", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), nil ])
+ end
+
def test_render_partial_with_empty_array_should_return_nil
assert_nil @view.render(:partial => [])
end
@@ -158,4 +166,14 @@ class ViewRenderTest < Test::Unit::TestCase
ActionView::Template.register_template_handler :foo, CustomHandler
assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
end
+
+ def test_render_with_layout
+ assert_equal %(<title></title>\nHello world!\n),
+ @view.render(:file => "test/hello_world.erb", :layout => "layouts/yield")
+ end
+
+ def test_render_with_nested_layout
+ assert_equal %(<title>title</title>\n<div id="column">column</div>\n<div id="content">content</div>\n),
+ @view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield")
+ end
end
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 7b94221ec0..1b28a59288 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -10,8 +10,8 @@ class TranslationHelperTest < Test::Unit::TestCase
end
def test_delegates_to_i18n_setting_the_raise_option
- I18n.expects(:translate).with(:foo, 'en-US', :raise => true)
- translate :foo, 'en-US'
+ I18n.expects(:translate).with(:foo, :locale => 'en-US', :raise => true)
+ translate :foo, :locale => 'en-US'
end
def test_returns_missing_translation_message_wrapped_into_span
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 85e967ac1c..2f6fa134b5 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -258,6 +258,16 @@ class UrlHelperTest < ActionView::TestCase
assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show")
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
+ @controller.url = "http://www.example.com/weblog/show"
+ assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
+ assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show")
+
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
+ @controller.url = "http://www.example.com/weblog/show?order=asc"
+ assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
+ assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=asc")
+
@controller.request = RequestMock.new("http://www.example.com/weblog/show")
@controller.url = "http://www.example.com/weblog/list"
assert_equal "<a href=\"http://www.example.com/weblog/list\">Listing</a>",
@@ -366,6 +376,19 @@ class UrlHelperWithControllerTest < ActionView::TestCase
assert_equal '/url_helper_with_controller/nil_url_for', @response.body
end
+ def test_named_route_should_show_host_and_path_using_controller_default_url_options
+ class << @controller
+ def default_url_options(options = nil)
+ {:host => 'testtwo.host'}
+ end
+ end
+
+ with_url_helper_routing do
+ get :show_named_route, :kind => 'url'
+ assert_equal 'http://testtwo.host/url_helper_with_controller/show_named_route', @response.body
+ end
+ end
+
protected
def with_url_helper_routing
with_routing do |set|
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index fec110d569..4ca062b535 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,3 +1,10 @@
+*2.2.1 [RC2 or 2.2 final]*
+
+* Ensure indices don't flip order in schema.rb #1266 [Jordi Bunster]
+
+* Fixed that serialized strings should never be type-casted (i.e. turning "Yes" to a boolean) #857 [Andreas Korth]
+
+
*2.2.0 [RC1] (October 24th, 2008)*
* Skip collection ids reader optimization if using :finder_sql [Jeremy Kemper]
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 52f6a04da1..84caca3518 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1596,16 +1596,19 @@ module ActiveRecord
reflection
end
+ mattr_accessor :valid_keys_for_has_and_belongs_to_many_association
+ @@valid_keys_for_has_and_belongs_to_many_association = [
+ :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
+ :select, :conditions, :include, :order, :group, :limit, :offset,
+ :uniq,
+ :finder_sql, :delete_sql, :insert_sql,
+ :before_add, :after_add, :before_remove, :after_remove,
+ :extend, :readonly,
+ :validate
+ ]
+
def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
- options.assert_valid_keys(
- :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
- :select, :conditions, :include, :order, :group, :limit, :offset,
- :uniq,
- :finder_sql, :delete_sql, :insert_sql,
- :before_add, :after_add, :before_remove, :after_remove,
- :extend, :readonly,
- :validate
- )
+ options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 1c753524de..177d156834 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -233,7 +233,7 @@ module ActiveRecord
method_name = method_id.to_s
if self.class.private_method_defined?(method_name)
- raise NoMethodError("Attempt to call private method", method_name, args)
+ raise NoMethodError.new("Attempt to call private method", method_name, args)
end
# If we haven't generated any methods yet, generate them, then
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 039e14435f..a36a137f0d 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1764,7 +1764,7 @@ module ActiveRecord #:nodoc:
#
# Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
# attempts to use it do not run through method_missing.
- def method_missing(method_id, *arguments)
+ def method_missing(method_id, *arguments, &block)
if match = DynamicFinderMatch.match(method_id)
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
@@ -1819,7 +1819,7 @@ module ActiveRecord #:nodoc:
end
end
}, __FILE__, __LINE__
- send(method_id, *arguments)
+ send(method_id, *arguments, &block)
end
else
super
@@ -2410,10 +2410,11 @@ module ActiveRecord #:nodoc:
# be made (since they can't be persisted).
def destroy
unless new_record?
- connection.delete <<-end_sql, "#{self.class.name} Destroy"
- DELETE FROM #{self.class.quoted_table_name}
- WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}
- end_sql
+ connection.delete(
+ "DELETE FROM #{self.class.quoted_table_name} " +
+ "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}",
+ "#{self.class.name} Destroy"
+ )
end
freeze
@@ -2938,7 +2939,7 @@ module ActiveRecord #:nodoc:
end
def object_from_yaml(string)
- return string unless string.is_a?(String)
+ return string unless string.is_a?(String) && string =~ /^---/
YAML::load(string) rescue string
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 2fc50b9bfa..950bd72101 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -15,7 +15,7 @@ module ActiveRecord
method_names.each do |method_name|
base.class_eval <<-end_code, __FILE__, __LINE__
def #{method_name}_with_query_dirty(*args)
- clear_query_cache if query_cache_enabled
+ clear_query_cache if @query_cache_enabled
#{method_name}_without_query_dirty(*args)
end
@@ -25,38 +25,24 @@ module ActiveRecord
end
end
- def query_cache_enabled
- Thread.current['query_cache_enabled']
- end
-
- def query_cache_enabled=(flag)
- Thread.current['query_cache_enabled'] = flag
- end
-
- def query_cache
- Thread.current['query_cache']
- end
-
- def query_cache=(cache)
- Thread.current['query_cache'] = cache
- end
+ attr_reader :query_cache, :query_cache_enabled
# Enable the query cache within the block.
def cache
- old, self.query_cache_enabled = query_cache_enabled, true
- self.query_cache ||= {}
+ old, @query_cache_enabled = @query_cache_enabled, true
+ @query_cache ||= {}
yield
ensure
clear_query_cache
- self.query_cache_enabled = old
+ @query_cache_enabled = old
end
# Disable the query cache within the block.
def uncached
- old, self.query_cache_enabled = query_cache_enabled, false
+ old, @query_cache_enabled = @query_cache_enabled, false
yield
ensure
- self.query_cache_enabled = old
+ @query_cache_enabled = old
end
# Clears the query cache.
@@ -66,11 +52,11 @@ module ActiveRecord
# the same SQL query and repeatedly return the same result each time, silently
# undermining the randomness you were expecting.
def clear_query_cache
- query_cache.clear if query_cache
+ @query_cache.clear if @query_cache
end
def select_all_with_query_cache(*args)
- if query_cache_enabled
+ if @query_cache_enabled
cache_sql(args.first) { select_all_without_query_cache(*args) }
else
select_all_without_query_cache(*args)
@@ -78,8 +64,8 @@ module ActiveRecord
end
def columns_with_query_cache(*args)
- if query_cache_enabled
- query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
+ if @query_cache_enabled
+ @query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
else
columns_without_query_cache(*args)
end
@@ -88,11 +74,11 @@ module ActiveRecord
private
def cache_sql(sql)
result =
- if query_cache.has_key?(sql)
+ if @query_cache.has_key?(sql)
log_info(sql, "CACHE", 0.0)
- query_cache[sql]
+ @query_cache[sql]
else
- query_cache[sql] = yield
+ @query_cache[sql] = yield
end
if Array === result
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index bebab5d05d..60ec01b95e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -68,72 +68,6 @@ module ActiveRecord
super
end
- # Escapes binary strings for bytea input to the database.
- def self.string_to_binary(value)
- if PGconn.respond_to?(:escape_bytea)
- self.class.module_eval do
- define_method(:string_to_binary) do |value|
- PGconn.escape_bytea(value) if value
- end
- end
- else
- self.class.module_eval do
- define_method(:string_to_binary) do |value|
- if value
- result = ''
- value.each_byte { |c| result << sprintf('\\\\%03o', c) }
- result
- end
- end
- end
- end
- self.class.string_to_binary(value)
- end
-
- # Unescapes bytea output from a database to the binary string it represents.
- def self.binary_to_string(value)
- # In each case, check if the value actually is escaped PostgreSQL bytea output
- # or an unescaped Active Record attribute that was just written.
- if PGconn.respond_to?(:unescape_bytea)
- self.class.module_eval do
- define_method(:binary_to_string) do |value|
- if value =~ /\\\d{3}/
- PGconn.unescape_bytea(value)
- else
- value
- end
- end
- end
- else
- self.class.module_eval do
- define_method(:binary_to_string) do |value|
- if value =~ /\\\d{3}/
- result = ''
- i, max = 0, value.size
- while i < max
- char = value[i]
- if char == ?\\
- if value[i+1] == ?\\
- char = ?\\
- i += 1
- else
- char = value[i+1..i+3].oct
- i += 3
- end
- end
- result << char
- i += 1
- end
- result
- else
- value
- end
- end
- end
- end
- self.class.binary_to_string(value)
- end
-
# Maps PostgreSQL-specific data types to logical Rails types.
def simplified_type(field_type)
case field_type
@@ -347,10 +281,78 @@ module ActiveRecord
# QUOTING ==================================================
+ # Escapes binary strings for bytea input to the database.
+ def escape_bytea(value)
+ if PGconn.respond_to?(:escape_bytea)
+ self.class.instance_eval do
+ define_method(:escape_bytea) do |value|
+ PGconn.escape_bytea(value) if value
+ end
+ end
+ else
+ self.class.instance_eval do
+ define_method(:escape_bytea) do |value|
+ if value
+ result = ''
+ value.each_byte { |c| result << sprintf('\\\\%03o', c) }
+ result
+ end
+ end
+ end
+ end
+ escape_bytea(value)
+ end
+
+ # Unescapes bytea output from a database to the binary string it represents.
+ # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
+ # on escaped binary output from database drive.
+ def unescape_bytea(value)
+ # In each case, check if the value actually is escaped PostgreSQL bytea output
+ # or an unescaped Active Record attribute that was just written.
+ if PGconn.respond_to?(:unescape_bytea)
+ self.class.instance_eval do
+ define_method(:unescape_bytea) do |value|
+ if value =~ /\\\d{3}/
+ PGconn.unescape_bytea(value)
+ else
+ value
+ end
+ end
+ end
+ else
+ self.class.instance_eval do
+ define_method(:unescape_bytea) do |value|
+ if value =~ /\\\d{3}/
+ result = ''
+ i, max = 0, value.size
+ while i < max
+ char = value[i]
+ if char == ?\\
+ if value[i+1] == ?\\
+ char = ?\\
+ i += 1
+ else
+ char = value[i+1..i+3].oct
+ i += 3
+ end
+ end
+ result << char
+ i += 1
+ end
+ result
+ else
+ value
+ end
+ end
+ end
+ end
+ unescape_bytea(value)
+ end
+
# Quotes PostgreSQL-specific data types for SQL input.
def quote(value, column = nil) #:nodoc:
if value.kind_of?(String) && column && column.type == :binary
- "#{quoted_string_prefix}'#{column.class.string_to_binary(value)}'"
+ "#{quoted_string_prefix}'#{escape_bytea(value)}'"
elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
"xml '#{quote_string(value)}'"
elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
@@ -463,11 +465,20 @@ module ActiveRecord
# create a 2D array representing the result set
def result_as_array(res) #:nodoc:
+ # check if we have any binary column and if they need escaping
+ unescape_col = []
+ for j in 0...res.nfields do
+ # unescape string passed BYTEA field (OID == 17)
+ unescape_col << ( res.ftype(j)==17 )
+ end
+
ary = []
for i in 0...res.ntuples do
ary << []
for j in 0...res.nfields do
- ary[i] << res.getvalue(i,j)
+ data = res.getvalue(i,j)
+ data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String)
+ ary[i] << data
end
end
return ary
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 4f96e225c1..2181bdf2dd 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -159,13 +159,19 @@ HEADER
end
def indexes(table, stream)
- indexes = @connection.indexes(table)
- indexes.each do |index|
- stream.print " add_index #{index.table.inspect}, #{index.columns.inspect}, :name => #{index.name.inspect}"
- stream.print ", :unique => true" if index.unique
+ if (indexes = @connection.indexes(table)).any?
+ add_index_statements = indexes.map do |index|
+ statment_parts = [ ('add_index ' + index.table.inspect) ]
+ statment_parts << index.columns.inspect
+ statment_parts << (':name => ' + index.name.inspect)
+ statment_parts << ':unique => true' if index.unique
+
+ ' ' + statment_parts.join(', ')
+ end
+
+ stream.puts add_index_statements.sort.join("\n")
stream.puts
end
- stream.puts unless indexes.empty?
end
end
-end
+end \ No newline at end of file
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 160716f944..77ee8d8fc4 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -233,8 +233,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "The pros and cons of programming naked.")
assert !topic.respond_to?(:title)
- assert_raise(NoMethodError) { topic.title }
- topic.send(:title)
+ exception = assert_raise(NoMethodError) { topic.title }
+ assert_equal "Attempt to call private method", exception.message
+ assert_equal "I'm private", topic.send(:title)
end
def test_write_attributes_respect_access_control
@@ -242,7 +243,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new
assert !topic.respond_to?(:title=)
- assert_raise(NoMethodError) { topic.title = "Pants"}
+ exception = assert_raise(NoMethodError) { topic.title = "Pants"}
+ assert_equal "Attempt to call private method", exception.message
topic.send(:title=, "Very large pants")
end
@@ -251,7 +253,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "Isaac Newton's pants")
assert !topic.respond_to?(:title?)
- assert_raise(NoMethodError) { topic.title? }
+ exception = assert_raise(NoMethodError) { topic.title? }
+ assert_equal "Attempt to call private method", exception.message
assert topic.send(:title?)
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index d512834237..da9f2742d8 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1429,6 +1429,12 @@ class BasicsTest < ActiveRecord::TestCase
topic = Topic.create("content" => myobj).reload
assert_equal(myobj, topic.content)
end
+
+ def test_serialized_string_attribute
+ myobj = "Yes"
+ topic = Topic.create("content" => myobj).reload
+ assert_equal(myobj, topic.content)
+ end
def test_nil_serialized_attribute_with_class_constraint
myobj = MyObject.new('value1', 'value2')
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 853474916c..153880afbd 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -846,6 +846,17 @@ class FinderTest < ActiveRecord::TestCase
assert !c.new_record?
end
+ def test_find_or_create_should_work_with_block_on_first_call
+ class << Company
+ undef_method(:find_or_create_by_name) if method_defined?(:find_or_create_by_name)
+ end
+ c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
+ assert_equal "Fortune 1000", c.name
+ assert_equal 1000.to_f, c.rating.to_f
+ assert c.valid?
+ assert !c.new_record?
+ end
+
def test_dynamic_find_or_initialize_from_one_attribute_caches_method
class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG
index 74ca71f65a..114a63c415 100644
--- a/activeresource/CHANGELOG
+++ b/activeresource/CHANGELOG
@@ -1,3 +1,8 @@
+*2.2.1 [RC2 or 2.2 final]*
+
+* Fixed that ActiveResource#post would post an empty string when it shouldn't be posting anything #525 [Paolo Angelini]
+
+
*2.2.0 [RC1] (October 24th, 2008)*
* Add ActiveResource::Base#to_xml and ActiveResource::Base#to_json. #1011 [Rasik Pandey, Cody Fauser]
diff --git a/activeresource/lib/active_resource/custom_methods.rb b/activeresource/lib/active_resource/custom_methods.rb
index 24306f251d..4647e8342c 100644
--- a/activeresource/lib/active_resource/custom_methods.rb
+++ b/activeresource/lib/active_resource/custom_methods.rb
@@ -90,7 +90,7 @@ module ActiveResource
end
def post(method_name, options = {}, body = nil)
- request_body = body.nil? ? encode : body
+ request_body = body.blank? ? encode : body
if new?
connection.post(custom_method_new_element_url(method_name, options), request_body, self.class.headers)
else
diff --git a/activeresource/test/base/custom_methods_test.rb b/activeresource/test/base/custom_methods_test.rb
index ba5799edfb..61887f4ec7 100644
--- a/activeresource/test/base/custom_methods_test.rb
+++ b/activeresource/test/base/custom_methods_test.rb
@@ -81,6 +81,8 @@ class CustomMethodsTest < Test::Unit::TestCase
# Test POST against a new element URL
ryan = Person.new(:name => 'Ryan')
assert_equal ActiveResource::Response.new(@ryan, 201, {'Location' => '/people/5.xml'}), ryan.post(:register)
+ expected_request = ActiveResource::Request.new(:post, '/people/new/register.xml', @ryan)
+ assert_equal expected_request.body, ActiveResource::HttpMock.requests.first.body
# Test POST against a nested collection URL
addy = StreetAddress.new(:street => '123 Test Dr.', :person_id => 1)
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 819a67adfa..e77affc315 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,3 +1,12 @@
+*2.2.1 [RC2 or 2.2 final]*
+
+* Added render :js for people who want to render inline JavaScript replies without using RJS [DHH]
+
+* Fixed the option merging in Array#to_xml #1126 [Rudolf Gavlas]
+
+* Make I18n::Backend::Simple reload its translations in development mode [DHH/Sven Fuchs]
+
+
*2.2.0 [RC1] (October 24th, 2008)*
* TimeWithZone#freeze: preload instance variables so that we can actually freeze [Geoff Buesing]
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 11c128da22..cf3e03f62c 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -172,7 +172,7 @@ module ActiveSupport #:nodoc:
else
xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) {
yield xml if block_given?
- each { |e| e.to_xml(opts.merge!({ :skip_instruct => true })) }
+ each { |e| e.to_xml(opts.merge({ :skip_instruct => true })) }
}
end
end
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index 950bca60a6..b4d8f61b8c 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -51,8 +51,8 @@ module ActiveSupport
private
def deprecation_message(callstack, message = nil)
- message ||= "You are using deprecated behavior which will be removed from Rails 2.0."
- "DEPRECATION WARNING: #{message} See http://www.rubyonrails.org/deprecation for details. #{deprecation_caller_message(callstack)}"
+ message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
+ "DEPRECATION WARNING: #{message}. #{deprecation_caller_message(callstack)}"
end
def deprecation_caller_message(callstack)
diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb
index cd722a3cfb..e6b1f39225 100644
--- a/activesupport/lib/active_support/string_inquirer.rb
+++ b/activesupport/lib/active_support/string_inquirer.rb
@@ -11,7 +11,7 @@ module ActiveSupport
#
class StringInquirer < String
def method_missing(method_name, *arguments)
- if method_name.to_s.ends_with?("?")
+ if method_name.to_s[-1,1] == "?"
self == method_name.to_s[0..-2]
else
super
diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb
index 344c77aecf..40e82d8225 100755
--- a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb
+++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb
@@ -67,6 +67,13 @@ module I18n
def load_path=(load_path)
@@load_path = load_path
end
+
+ # Tells the backend to reload translations. Used in situations like the
+ # Rails development environment. Backends can implement whatever strategy
+ # is useful.
+ def reload!
+ backend.reload!
+ end
# Translates, pluralizes and interpolates a given key using a given locale,
# scope, and default, as well as interpolation values.
@@ -150,10 +157,10 @@ module I18n
# I18n.t [:foo, :bar], :scope => :baz
def translate(key, options = {})
locale = options.delete(:locale) || I18n.locale
- backend.translate locale, key, options
+ backend.translate(locale, key, options)
rescue I18n::ArgumentError => e
raise e if options[:raise]
- send @@exception_handler, e, locale, key, options
+ send(@@exception_handler, e, locale, key, options)
end
alias :t :translate
@@ -180,8 +187,8 @@ module I18n
# keys are Symbols.
def normalize_translation_keys(locale, key, scope)
keys = [locale] + Array(scope) + [key]
- keys = keys.map{|k| k.to_s.split(/\./) }
- keys.flatten.map{|k| k.to_sym}
+ keys = keys.map { |k| k.to_s.split(/\./) }
+ keys.flatten.map { |k| k.to_sym }
end
end
end \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
index 30e3655b7b..bdda55d3fe 100644
--- a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
+++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
@@ -10,7 +10,7 @@ module I18n
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
# for details.
def load_translations(*filenames)
- filenames.each {|filename| load_file filename }
+ filenames.each { |filename| load_file(filename) }
end
# Stores translations for the given locale in memory.
@@ -23,12 +23,12 @@ module I18n
def translate(locale, key, options = {})
raise InvalidLocale.new(locale) if locale.nil?
- return key.map{|k| translate locale, k, options } if key.is_a? Array
+ return key.map { |k| translate(locale, k, options) } if key.is_a? Array
reserved = :scope, :default
count, scope, default = options.values_at(:count, *reserved)
options.delete(:default)
- values = options.reject{|name, value| reserved.include? name }
+ values = options.reject { |name, value| reserved.include?(name) }
entry = lookup(locale, key, scope)
if entry.nil?
@@ -37,8 +37,8 @@ module I18n
raise(I18n::MissingTranslationData.new(locale, key, options))
end
end
- entry = pluralize locale, entry, count
- entry = interpolate locale, entry, values
+ entry = pluralize(locale, entry, count)
+ entry = interpolate(locale, entry, values)
entry
end
@@ -69,8 +69,12 @@ module I18n
@initialized ||= false
end
- protected
+ def reload!
+ @initialized = false
+ @translations = nil
+ end
+ protected
def init_translations
load_translations(*I18n.load_path)
@initialized = true
@@ -88,7 +92,7 @@ module I18n
def lookup(locale, key, scope = [])
return unless key
init_translations unless initialized?
- keys = I18n.send :normalize_translation_keys, locale, key, scope
+ keys = I18n.send(:normalize_translation_keys, locale, key, scope)
keys.inject(translations) do |result, k|
if (x = result[k.to_sym]).nil?
return nil
@@ -170,21 +174,21 @@ module I18n
# for all other file extensions.
def load_file(filename)
type = File.extname(filename).tr('.', '').downcase
- raise UnknownFileType.new(type, filename) unless respond_to? :"load_#{type}"
+ raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}")
data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash
- data.each{|locale, d| merge_translations locale, d }
+ data.each { |locale, d| merge_translations(locale, d) }
end
# Loads a plain Ruby translations file. eval'ing the file must yield
# a Hash containing translation data with locales as toplevel keys.
def load_rb(filename)
- eval IO.read(filename), binding, filename
+ eval(IO.read(filename), binding, filename)
end
# Loads a YAML translations file. The data must have locales as
# toplevel keys.
def load_yml(filename)
- YAML::load IO.read(filename)
+ YAML::load(IO.read(filename))
end
# Deep merges the given translations hash with the existing translations
@@ -192,16 +196,16 @@ module I18n
def merge_translations(locale, data)
locale = locale.to_sym
translations[locale] ||= {}
- data = deep_symbolize_keys data
+ data = deep_symbolize_keys(data)
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
- merger = proc{|key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
- translations[locale].merge! data, &merger
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
+ translations[locale].merge!(data, &merger)
end
# Return a new hash with all keys and nested keys converted to symbols.
def deep_symbolize_keys(hash)
- hash.inject({}){|result, (key, value)|
+ hash.inject({}) { |result, (key, value)|
value = deep_symbolize_keys(value) if value.is_a? Hash
result[(key.to_sym rescue key) || key] = value
result
@@ -209,4 +213,4 @@ module I18n
end
end
end
-end
+end \ No newline at end of file
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 390da3b890..058afddbde 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,3 +1,10 @@
+*2.2.1 [RC2 or 2.2 final]*
+
+* Fixed plugin generator so that generated unit tests would subclass ActiveSupport::TestCase, also introduced a helper script to reduce the needed require statements #1137 [Mathias Meyer]
+
+* Update Prototype to 1.6.0.3 [sam]
+
+
*2.2.0 [RC1] (October 24th, 2008)*
* Fixed that sqlite would report "db/development.sqlite3 already exists" whether true or not on db:create #614 [Antonio Cangiano]
diff --git a/railties/Rakefile b/railties/Rakefile
index adb6db0b64..872ea83ec2 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -192,7 +192,7 @@ task :copy_configs do
app_name = "rails"
socket = nil
require 'erb'
- File.open("#{PKG_DESTINATION}/config/database.yml", 'w') {|f| f.write ERB.new(IO.read("configs/databases/mysql.yml"), nil, '-').result(binding)}
+ File.open("#{PKG_DESTINATION}/config/database.yml", 'w') {|f| f.write ERB.new(IO.read("configs/databases/sqlite3.yml"), nil, '-').result(binding)}
cp "configs/routes.rb", "#{PKG_DESTINATION}/config/routes.rb"
diff --git a/railties/doc/guides/source/active_record_basics.txt b/railties/doc/guides/source/active_record_basics.txt
index 11a7bdb302..15fc544f25 100644
--- a/railties/doc/guides/source/active_record_basics.txt
+++ b/railties/doc/guides/source/active_record_basics.txt
@@ -178,4 +178,4 @@ Rails has a reputation of being a zero-config framework which means that it aim
* (6) before_create
* (-) create
* (7) after_create
- * (8) after_save \ No newline at end of file
+ * (8) after_save
diff --git a/railties/html/javascripts/prototype.js b/railties/html/javascripts/prototype.js
index 2c70b8a7e8..dfe8ab4e13 100644
--- a/railties/html/javascripts/prototype.js
+++ b/railties/html/javascripts/prototype.js
@@ -1,4 +1,4 @@
-/* Prototype JavaScript framework, version 1.6.0.2
+/* Prototype JavaScript framework, version 1.6.0.3
* (c) 2005-2008 Sam Stephenson
*
* Prototype is freely distributable under the terms of an MIT-style license.
@@ -7,23 +7,26 @@
*--------------------------------------------------------------------------*/
var Prototype = {
- Version: '1.6.0.2',
+ Version: '1.6.0.3',
Browser: {
- IE: !!(window.attachEvent && !window.opera),
- Opera: !!window.opera,
+ IE: !!(window.attachEvent &&
+ navigator.userAgent.indexOf('Opera') === -1),
+ Opera: navigator.userAgent.indexOf('Opera') > -1,
WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
- Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 &&
+ navigator.userAgent.indexOf('KHTML') === -1,
MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
},
BrowserFeatures: {
XPath: !!document.evaluate,
+ SelectorsAPI: !!document.querySelector,
ElementExtensions: !!window.HTMLElement,
SpecificElementExtensions:
- document.createElement('div').__proto__ &&
- document.createElement('div').__proto__ !==
- document.createElement('form').__proto__
+ document.createElement('div')['__proto__'] &&
+ document.createElement('div')['__proto__'] !==
+ document.createElement('form')['__proto__']
},
ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
@@ -83,12 +86,13 @@ Class.Methods = {
var property = properties[i], value = source[property];
if (ancestor && Object.isFunction(value) &&
value.argumentNames().first() == "$super") {
- var method = value, value = Object.extend((function(m) {
+ var method = value;
+ value = (function(m) {
return function() { return ancestor[m].apply(this, arguments) };
- })(property).wrap(method), {
- valueOf: function() { return method },
- toString: function() { return method.toString() }
- });
+ })(property).wrap(method);
+
+ value.valueOf = method.valueOf.bind(method);
+ value.toString = method.toString.bind(method);
}
this.prototype[property] = value;
}
@@ -167,7 +171,7 @@ Object.extend(Object, {
},
isElement: function(object) {
- return object && object.nodeType == 1;
+ return !!(object && object.nodeType == 1);
},
isArray: function(object) {
@@ -198,7 +202,8 @@ Object.extend(Object, {
Object.extend(Function.prototype, {
argumentNames: function() {
- var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+ var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+ .replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
},
@@ -232,6 +237,11 @@ Object.extend(Function.prototype, {
}, timeout);
},
+ defer: function() {
+ var args = [0.01].concat($A(arguments));
+ return this.delay.apply(this, args);
+ },
+
wrap: function(wrapper) {
var __method = this;
return function() {
@@ -248,8 +258,6 @@ Object.extend(Function.prototype, {
}
});
-Function.prototype.defer = Function.prototype.delay.curry(0.01);
-
Date.prototype.toJSON = function() {
return '"' + this.getUTCFullYear() + '-' +
(this.getUTCMonth() + 1).toPaddedString(2) + '-' +
@@ -530,7 +538,7 @@ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.proto
return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
},
unescapeHTML: function() {
- return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+ return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
}
});
@@ -547,7 +555,7 @@ Object.extend(String.prototype.escapeHTML, {
text: document.createTextNode('')
});
-with (String.prototype.escapeHTML) div.appendChild(text);
+String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
var Template = Class.create({
initialize: function(template, pattern) {
@@ -589,10 +597,9 @@ var $break = { };
var Enumerable = {
each: function(iterator, context) {
var index = 0;
- iterator = iterator.bind(context);
try {
this._each(function(value) {
- iterator(value, index++);
+ iterator.call(context, value, index++);
});
} catch (e) {
if (e != $break) throw e;
@@ -601,47 +608,46 @@ var Enumerable = {
},
eachSlice: function(number, iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
var index = -number, slices = [], array = this.toArray();
+ if (number < 1) return array;
while ((index += number) < array.length)
slices.push(array.slice(index, index+number));
return slices.collect(iterator, context);
},
all: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ iterator = iterator || Prototype.K;
var result = true;
this.each(function(value, index) {
- result = result && !!iterator(value, index);
+ result = result && !!iterator.call(context, value, index);
if (!result) throw $break;
});
return result;
},
any: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ iterator = iterator || Prototype.K;
var result = false;
this.each(function(value, index) {
- if (result = !!iterator(value, index))
+ if (result = !!iterator.call(context, value, index))
throw $break;
});
return result;
},
collect: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ iterator = iterator || Prototype.K;
var results = [];
this.each(function(value, index) {
- results.push(iterator(value, index));
+ results.push(iterator.call(context, value, index));
});
return results;
},
detect: function(iterator, context) {
- iterator = iterator.bind(context);
var result;
this.each(function(value, index) {
- if (iterator(value, index)) {
+ if (iterator.call(context, value, index)) {
result = value;
throw $break;
}
@@ -650,17 +656,16 @@ var Enumerable = {
},
findAll: function(iterator, context) {
- iterator = iterator.bind(context);
var results = [];
this.each(function(value, index) {
- if (iterator(value, index))
+ if (iterator.call(context, value, index))
results.push(value);
});
return results;
},
grep: function(filter, iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ iterator = iterator || Prototype.K;
var results = [];
if (Object.isString(filter))
@@ -668,7 +673,7 @@ var Enumerable = {
this.each(function(value, index) {
if (filter.match(value))
- results.push(iterator(value, index));
+ results.push(iterator.call(context, value, index));
});
return results;
},
@@ -696,9 +701,8 @@ var Enumerable = {
},
inject: function(memo, iterator, context) {
- iterator = iterator.bind(context);
this.each(function(value, index) {
- memo = iterator(memo, value, index);
+ memo = iterator.call(context, memo, value, index);
});
return memo;
},
@@ -711,10 +715,10 @@ var Enumerable = {
},
max: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
- value = iterator(value, index);
+ value = iterator.call(context, value, index);
if (result == null || value >= result)
result = value;
});
@@ -722,10 +726,10 @@ var Enumerable = {
},
min: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
- value = iterator(value, index);
+ value = iterator.call(context, value, index);
if (result == null || value < result)
result = value;
});
@@ -733,10 +737,10 @@ var Enumerable = {
},
partition: function(iterator, context) {
- iterator = iterator ? iterator.bind(context) : Prototype.K;
+ iterator = iterator || Prototype.K;
var trues = [], falses = [];
this.each(function(value, index) {
- (iterator(value, index) ?
+ (iterator.call(context, value, index) ?
trues : falses).push(value);
});
return [trues, falses];
@@ -751,19 +755,20 @@ var Enumerable = {
},
reject: function(iterator, context) {
- iterator = iterator.bind(context);
var results = [];
this.each(function(value, index) {
- if (!iterator(value, index))
+ if (!iterator.call(context, value, index))
results.push(value);
});
return results;
},
sortBy: function(iterator, context) {
- iterator = iterator.bind(context);
return this.map(function(value, index) {
- return {value: value, criteria: iterator(value, index)};
+ return {
+ value: value,
+ criteria: iterator.call(context, value, index)
+ };
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
@@ -815,8 +820,12 @@ function $A(iterable) {
if (Prototype.Browser.WebKit) {
$A = function(iterable) {
if (!iterable) return [];
- if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
- iterable.toArray) return iterable.toArray();
+ // In Safari, only use the `toArray` method if it's not a NodeList.
+ // A NodeList is a function, has an function `item` property, and a numeric
+ // `length` property. Adapted from Google Doctype.
+ if (!(typeof iterable === 'function' && typeof iterable.length ===
+ 'number' && typeof iterable.item === 'function') && iterable.toArray)
+ return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
@@ -963,8 +972,8 @@ Object.extend(Number.prototype, {
return this + 1;
},
- times: function(iterator) {
- $R(0, this, true).each(iterator);
+ times: function(iterator, context) {
+ $R(0, this, true).each(iterator, context);
return this;
},
@@ -1011,7 +1020,9 @@ var Hash = Class.create(Enumerable, (function() {
},
get: function(key) {
- return this._object[key];
+ // simulating poorly supported hasOwnProperty
+ if (this._object[key] !== Object.prototype[key])
+ return this._object[key];
},
unset: function(key) {
@@ -1051,14 +1062,14 @@ var Hash = Class.create(Enumerable, (function() {
},
toQueryString: function() {
- return this.map(function(pair) {
+ return this.inject([], function(results, pair) {
var key = encodeURIComponent(pair.key), values = pair.value;
if (values && typeof values == 'object') {
if (Object.isArray(values))
- return values.map(toQueryPair.curry(key)).join('&');
- }
- return toQueryPair(key, values);
+ return results.concat(values.map(toQueryPair.curry(key)));
+ } else results.push(toQueryPair(key, values));
+ return results;
}).join('&');
},
@@ -1558,6 +1569,7 @@ if (!Node.ELEMENT_NODE) {
return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
};
Object.extend(this.Element, element || { });
+ if (element) this.Element.prototype = element.prototype;
}).call(window);
Element.cache = { };
@@ -1574,12 +1586,14 @@ Element.Methods = {
},
hide: function(element) {
- $(element).style.display = 'none';
+ element = $(element);
+ element.style.display = 'none';
return element;
},
show: function(element) {
- $(element).style.display = '';
+ element = $(element);
+ element.style.display = '';
return element;
},
@@ -1733,7 +1747,7 @@ Element.Methods = {
element = $(element);
if (arguments.length == 1) return element.firstDescendant();
return Object.isNumber(expression) ? element.descendants()[expression] :
- element.select(expression)[index || 0];
+ Element.select(element, expression)[index || 0];
},
previous: function(element, expression, index) {
@@ -1863,24 +1877,16 @@ Element.Methods = {
descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
- var originalAncestor = ancestor;
if (element.compareDocumentPosition)
return (element.compareDocumentPosition(ancestor) & 8) === 8;
- if (element.sourceIndex && !Prototype.Browser.Opera) {
- var e = element.sourceIndex, a = ancestor.sourceIndex,
- nextAncestor = ancestor.nextSibling;
- if (!nextAncestor) {
- do { ancestor = ancestor.parentNode; }
- while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
- }
- if (nextAncestor && nextAncestor.sourceIndex)
- return (e > a && e < nextAncestor.sourceIndex);
- }
+ if (ancestor.contains)
+ return ancestor.contains(element) && ancestor !== element;
while (element = element.parentNode)
- if (element == originalAncestor) return true;
+ if (element == ancestor) return true;
+
return false;
},
@@ -1895,7 +1901,7 @@ Element.Methods = {
element = $(element);
style = style == 'float' ? 'cssFloat' : style.camelize();
var value = element.style[style];
- if (!value) {
+ if (!value || value == 'auto') {
var css = document.defaultView.getComputedStyle(element, null);
value = css ? css[style] : null;
}
@@ -1934,7 +1940,7 @@ Element.Methods = {
getDimensions: function(element) {
element = $(element);
- var display = $(element).getStyle('display');
+ var display = element.getStyle('display');
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};
@@ -1963,7 +1969,7 @@ Element.Methods = {
element.style.position = 'relative';
// Opera returns the offset relative to the positioning context, when an
// element is position relative but top and left have not been defined
- if (window.opera) {
+ if (Prototype.Browser.Opera) {
element.style.top = 0;
element.style.left = 0;
}
@@ -2018,7 +2024,7 @@ Element.Methods = {
valueL += element.offsetLeft || 0;
element = element.offsetParent;
if (element) {
- if (element.tagName == 'BODY') break;
+ if (element.tagName.toUpperCase() == 'BODY') break;
var p = Element.getStyle(element, 'position');
if (p !== 'static') break;
}
@@ -2028,7 +2034,7 @@ Element.Methods = {
absolutize: function(element) {
element = $(element);
- if (element.getStyle('position') == 'absolute') return;
+ if (element.getStyle('position') == 'absolute') return element;
// Position.prepare(); // To be done manually by Scripty when it needs it.
var offsets = element.positionedOffset();
@@ -2052,7 +2058,7 @@ Element.Methods = {
relativize: function(element) {
element = $(element);
- if (element.getStyle('position') == 'relative') return;
+ if (element.getStyle('position') == 'relative') return element;
// Position.prepare(); // To be done manually by Scripty when it needs it.
element.style.position = 'relative';
@@ -2103,7 +2109,7 @@ Element.Methods = {
element = forElement;
do {
- if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+ if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
valueT -= element.scrollTop || 0;
valueL -= element.scrollLeft || 0;
}
@@ -2218,6 +2224,9 @@ else if (Prototype.Browser.IE) {
Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
function(proceed, element) {
element = $(element);
+ // IE throws an error if element is not in document
+ try { element.offsetParent }
+ catch(e) { return $(document.body) }
var position = element.getStyle('position');
if (position !== 'static') return proceed(element);
element.setStyle({ position: 'relative' });
@@ -2231,6 +2240,8 @@ else if (Prototype.Browser.IE) {
Element.Methods[method] = Element.Methods[method].wrap(
function(proceed, element) {
element = $(element);
+ try { element.offsetParent }
+ catch(e) { return Element._returnOffset(0,0) }
var position = element.getStyle('position');
if (position !== 'static') return proceed(element);
// Trigger hasLayout on the offset parent so that IE6 reports
@@ -2246,6 +2257,14 @@ else if (Prototype.Browser.IE) {
);
});
+ Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
+ function(proceed, element) {
+ try { element.offsetParent }
+ catch(e) { return Element._returnOffset(0,0) }
+ return proceed(element);
+ }
+ );
+
Element.Methods.getStyle = function(element, style) {
element = $(element);
style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
@@ -2337,7 +2356,7 @@ else if (Prototype.Browser.IE) {
Element._attributeTranslations.has = {};
$w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
- 'encType maxLength readOnly longDesc').each(function(attr) {
+ 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
Element._attributeTranslations.has[attr.toLowerCase()] = attr;
});
@@ -2390,7 +2409,7 @@ else if (Prototype.Browser.WebKit) {
(value < 0.00001) ? 0 : value;
if (value == 1)
- if(element.tagName == 'IMG' && element.width) {
+ if(element.tagName.toUpperCase() == 'IMG' && element.width) {
element.width++; element.width--;
} else try {
var n = document.createTextNode(' ');
@@ -2521,7 +2540,7 @@ Element.Methods.Simulated = {
hasAttribute: function(element, attribute) {
attribute = Element._attributeTranslations.has[attribute] || attribute;
var node = $(element).getAttributeNode(attribute);
- return node && node.specified;
+ return !!(node && node.specified);
}
};
@@ -2530,9 +2549,9 @@ Element.Methods.ByTag = { };
Object.extend(Element, Element.Methods);
if (!Prototype.BrowserFeatures.ElementExtensions &&
- document.createElement('div').__proto__) {
+ document.createElement('div')['__proto__']) {
window.HTMLElement = { };
- window.HTMLElement.prototype = document.createElement('div').__proto__;
+ window.HTMLElement.prototype = document.createElement('div')['__proto__'];
Prototype.BrowserFeatures.ElementExtensions = true;
}
@@ -2547,7 +2566,7 @@ Element.extend = (function() {
element.nodeType != 1 || element == window) return element;
var methods = Object.clone(Methods),
- tagName = element.tagName, property, value;
+ tagName = element.tagName.toUpperCase(), property, value;
// extend methods for specific tags
if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
@@ -2643,7 +2662,7 @@ Element.addMethods = function(methods) {
if (window[klass]) return window[klass];
window[klass] = { };
- window[klass].prototype = document.createElement(tagName).__proto__;
+ window[klass].prototype = document.createElement(tagName)['__proto__'];
return window[klass];
}
@@ -2669,12 +2688,18 @@ Element.addMethods = function(methods) {
document.viewport = {
getDimensions: function() {
- var dimensions = { };
- var B = Prototype.Browser;
+ var dimensions = { }, B = Prototype.Browser;
$w('width height').each(function(d) {
var D = d.capitalize();
- dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
- (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+ if (B.WebKit && !document.evaluate) {
+ // Safari <3.0 needs self.innerWidth/Height
+ dimensions[d] = self['inner' + D];
+ } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
+ // Opera <9.5 needs document.body.clientWidth/Height
+ dimensions[d] = document.body['client' + D]
+ } else {
+ dimensions[d] = document.documentElement['client' + D];
+ }
});
return dimensions;
},
@@ -2693,14 +2718,24 @@ document.viewport = {
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
}
};
-/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
* part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
* license. Please see http://www.yui-ext.com/ for more information. */
var Selector = Class.create({
initialize: function(expression) {
this.expression = expression.strip();
- this.compileMatcher();
+
+ if (this.shouldUseSelectorsAPI()) {
+ this.mode = 'selectorsAPI';
+ } else if (this.shouldUseXPath()) {
+ this.mode = 'xpath';
+ this.compileXPathMatcher();
+ } else {
+ this.mode = "normal";
+ this.compileMatcher();
+ }
+
},
shouldUseXPath: function() {
@@ -2715,16 +2750,29 @@ var Selector = Class.create({
// XPath can't do namespaced attributes, nor can it read
// the "checked" property from DOM nodes
- if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+ if ((/(\[[\w-]*?:|:checked)/).test(e))
return false;
return true;
},
- compileMatcher: function() {
- if (this.shouldUseXPath())
- return this.compileXPathMatcher();
+ shouldUseSelectorsAPI: function() {
+ if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
+
+ if (!Selector._div) Selector._div = new Element('div');
+
+ // Make sure the browser treats the selector as valid. Test on an
+ // isolated element to minimize cost of this check.
+ try {
+ Selector._div.querySelector(this.expression);
+ } catch(e) {
+ return false;
+ }
+
+ return true;
+ },
+ compileMatcher: function() {
var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
c = Selector.criteria, le, p, m;
@@ -2742,7 +2790,7 @@ var Selector = Class.create({
p = ps[i];
if (m = e.match(p)) {
this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
- new Template(c[i]).evaluate(m));
+ new Template(c[i]).evaluate(m));
e = e.replace(m[0], '');
break;
}
@@ -2781,8 +2829,27 @@ var Selector = Class.create({
findElements: function(root) {
root = root || document;
- if (this.xpath) return document._getElementsByXPath(this.xpath, root);
- return this.matcher(root);
+ var e = this.expression, results;
+
+ switch (this.mode) {
+ case 'selectorsAPI':
+ // querySelectorAll queries document-wide, then filters to descendants
+ // of the context element. That's not what we want.
+ // Add an explicit context to the selector if necessary.
+ if (root !== document) {
+ var oldId = root.id, id = $(root).identify();
+ e = "#" + id + " " + e;
+ }
+
+ results = $A(root.querySelectorAll(e)).map(Element.extend);
+ root.id = oldId;
+
+ return results;
+ case 'xpath':
+ return document._getElementsByXPath(this.xpath, root);
+ default:
+ return this.matcher(root);
+ }
},
match: function(element) {
@@ -2873,10 +2940,10 @@ Object.extend(Selector, {
'first-child': '[not(preceding-sibling::*)]',
'last-child': '[not(following-sibling::*)]',
'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
- 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'empty': "[count(*) = 0 and (count(text()) = 0)]",
'checked': "[@checked]",
- 'disabled': "[@disabled]",
- 'enabled': "[not(@disabled)]",
+ 'disabled': "[(@disabled) and (@type!='hidden')]",
+ 'enabled': "[not(@disabled) and (@type!='hidden')]",
'not': function(m) {
var e = m[6], p = Selector.patterns,
x = Selector.xpath, le, v;
@@ -2968,7 +3035,7 @@ Object.extend(Selector, {
className: /^\.([\w\-\*]+)(\b|$)/,
pseudo:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
- attrPresence: /^\[([\w]+)\]/,
+ attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
},
@@ -3081,7 +3148,7 @@ Object.extend(Selector, {
nextElementSibling: function(node) {
while (node = node.nextSibling)
- if (node.nodeType == 1) return node;
+ if (node.nodeType == 1) return node;
return null;
},
@@ -3270,7 +3337,7 @@ Object.extend(Selector, {
'empty': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
// IE treats comments as element nodes
- if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ if (node.tagName == '!' || node.firstChild) continue;
results.push(node);
}
return results;
@@ -3288,7 +3355,8 @@ Object.extend(Selector, {
'enabled': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!node.disabled) results.push(node);
+ if (!node.disabled && (!node.type || node.type !== 'hidden'))
+ results.push(node);
return results;
},
@@ -3308,11 +3376,14 @@ Object.extend(Selector, {
operators: {
'=': function(nv, v) { return nv == v; },
'!=': function(nv, v) { return nv != v; },
- '^=': function(nv, v) { return nv.startsWith(v); },
+ '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
+ '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
+ '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
'$=': function(nv, v) { return nv.endsWith(v); },
'*=': function(nv, v) { return nv.include(v); },
'~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
- '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+ '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
+ '-').include('-' + (v || "").toUpperCase() + '-'); }
},
split: function(expression) {
@@ -3386,7 +3457,7 @@ var Form = {
var data = elements.inject({ }, function(result, element) {
if (!element.disabled && element.name) {
key = element.name; value = $(element).getValue();
- if (value != null && (element.type != 'submit' || (!submitted &&
+ if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
submit !== false && (!submit || key == submit) && (submitted = true)))) {
if (key in result) {
// a key is already present; construct an array of values
@@ -3547,7 +3618,6 @@ Form.Element.Methods = {
disable: function(element) {
element = $(element);
- element.blur();
element.disabled = true;
return element;
},
@@ -3587,22 +3657,22 @@ Form.Element.Serializers = {
else element.value = value;
},
- select: function(element, index) {
- if (Object.isUndefined(index))
+ select: function(element, value) {
+ if (Object.isUndefined(value))
return this[element.type == 'select-one' ?
'selectOne' : 'selectMany'](element);
else {
- var opt, value, single = !Object.isArray(index);
+ var opt, currentValue, single = !Object.isArray(value);
for (var i = 0, length = element.length; i < length; i++) {
opt = element.options[i];
- value = this.optionValue(opt);
+ currentValue = this.optionValue(opt);
if (single) {
- if (value == index) {
+ if (currentValue == value) {
opt.selected = true;
return;
}
}
- else opt.selected = index.include(value);
+ else opt.selected = value.include(currentValue);
}
}
},
@@ -3773,8 +3843,23 @@ Event.Methods = (function() {
isRightClick: function(event) { return isButton(event, 2) },
element: function(event) {
- var node = Event.extend(event).target;
- return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+ event = Event.extend(event);
+
+ var node = event.target,
+ type = event.type,
+ currentTarget = event.currentTarget;
+
+ if (currentTarget && currentTarget.tagName) {
+ // Firefox screws up the "click" event when moving between radio buttons
+ // via arrow keys. It also screws up the "load" and "error" events on images,
+ // reporting the document as the target instead of the original image.
+ if (type === 'load' || type === 'error' ||
+ (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+ && currentTarget.type === 'radio'))
+ node = currentTarget;
+ }
+ if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
+ return Element.extend(node);
},
findElement: function(event, expression) {
@@ -3785,11 +3870,15 @@ Event.Methods = (function() {
},
pointer: function(event) {
+ var docElement = document.documentElement,
+ body = document.body || { scrollLeft: 0, scrollTop: 0 };
return {
x: event.pageX || (event.clientX +
- (document.documentElement.scrollLeft || document.body.scrollLeft)),
+ (docElement.scrollLeft || body.scrollLeft) -
+ (docElement.clientLeft || 0)),
y: event.pageY || (event.clientY +
- (document.documentElement.scrollTop || document.body.scrollTop))
+ (docElement.scrollTop || body.scrollTop) -
+ (docElement.clientTop || 0))
};
},
@@ -3834,7 +3923,7 @@ Event.extend = (function() {
};
} else {
- Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
Object.extend(Event.prototype, methods);
return Prototype.K;
}
@@ -3899,10 +3988,20 @@ Object.extend(Event, (function() {
cache[id][eventName] = null;
}
+
+ // Internet Explorer needs to remove event handlers on page unload
+ // in order to avoid memory leaks.
if (window.attachEvent) {
window.attachEvent("onunload", destroyCache);
}
+ // Safari has a dummy event handler on page unload so that it won't
+ // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
+ // object when page is returned to via the back button using its bfcache.
+ if (Prototype.Browser.WebKit) {
+ window.addEventListener('unload', Prototype.emptyFunction, false);
+ }
+
return {
observe: function(element, eventName, handler) {
element = $(element);
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index 6500b2d309..e3a0e3bad1 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -267,6 +267,7 @@ module Rails
end
def add_gem_load_paths
+ Rails::GemDependency.add_frozen_gem_path
unless @configuration.gems.empty?
require "rubygems"
@configuration.gems.each { |gem| gem.add_load_paths }
diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb
index 46d5fd3a47..cd280ac023 100644
--- a/railties/lib/rails/gem_dependency.rb
+++ b/railties/lib/rails/gem_dependency.rb
@@ -18,11 +18,14 @@ module Rails
def self.add_frozen_gem_path
@@paths_loaded ||= begin
- Gem.source_index = Rails::VendorGemSourceIndex.new(Gem.source_index)
+ source_index = Rails::VendorGemSourceIndex.new(Gem.source_index)
+ Gem.clear_paths
+ Gem.source_index = source_index
# loaded before us - we can't change them, so mark them
Gem.loaded_specs.each do |name, spec|
@@framework_gems[name] = spec
end
+ true
end
end
@@ -170,19 +173,27 @@ module Rails
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."
+ if File.exist?(File.dirname(spec_filename(directory)))
+ 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
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."
+ if framework_gem?
+ puts "Gem directory for #{name} not found - check if it's loading before rails."
+ else
+ puts "Something bad is going on - gem directory not found for #{name}."
+ end
end
end
diff --git a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb b/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
index 615c575e6e..6826998252 100644
--- a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
+++ b/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb
@@ -16,16 +16,16 @@ class PluginGenerator < Rails::Generator::NamedBase
m.directory "#{plugin_path}/tasks"
m.directory "#{plugin_path}/test"
- m.template 'README', "#{plugin_path}/README"
- m.template 'MIT-LICENSE', "#{plugin_path}/MIT-LICENSE"
- m.template 'Rakefile', "#{plugin_path}/Rakefile"
- m.template 'init.rb', "#{plugin_path}/init.rb"
- m.template 'install.rb', "#{plugin_path}/install.rb"
- m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb"
- m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb"
- m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake"
- m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb"
-
+ m.template 'README', "#{plugin_path}/README"
+ m.template 'MIT-LICENSE', "#{plugin_path}/MIT-LICENSE"
+ m.template 'Rakefile', "#{plugin_path}/Rakefile"
+ m.template 'init.rb', "#{plugin_path}/init.rb"
+ m.template 'install.rb', "#{plugin_path}/install.rb"
+ m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb"
+ m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb"
+ m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake"
+ m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb"
+ m.template 'test_helper.rb', "#{plugin_path}/test/test_helper.rb"
if @with_generator
m.directory "#{plugin_path}/generators"
m.directory "#{plugin_path}/generators/#{file_name}"
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile b/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile
index 1824fb10fe..85e8ff1834 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile
+++ b/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile
@@ -8,6 +8,7 @@ task :default => :test
desc 'Test the <%= file_name %> plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
+ t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb b/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb
new file mode 100644
index 0000000000..cf148b8b47
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb
@@ -0,0 +1,3 @@
+require 'rubygems'
+require 'active_support'
+require 'active_support/test_case' \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb b/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb
index 6ede6ef1d2..3e0bc29d3a 100644
--- a/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb
+++ b/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb
@@ -1,6 +1,6 @@
-require 'test/unit'
+require 'test_helper'
-class <%= class_name %>Test < Test::Unit::TestCase
+class <%= class_name %>Test < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
diff --git a/railties/lib/tasks/gems.rake b/railties/lib/tasks/gems.rake
index e2cb4b9577..754e3ba5c9 100644
--- a/railties/lib/tasks/gems.rake
+++ b/railties/lib/tasks/gems.rake
@@ -6,10 +6,11 @@ task :gems => 'gems:base' do
puts
puts "I = Installed"
puts "F = Frozen"
+ puts "R = Framework (loaded before rails starts)"
end
def print_gem_status(gem, indent=1)
- code = gem.loaded? ? (gem.frozen? ? "F" : "I") : " "
+ code = gem.loaded? ? (gem.frozen? ? (gem.framework_gem? ? "R" : "F") : "I") : " "
puts " "*(indent-1)+" - [#{code}] #{gem.name} #{gem.requirement.to_s}"
gem.dependencies.each { |g| print_gem_status(g, indent+1)} if gem.loaded?
end
diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb
index 5f026b2616..4f9e824c9f 100644
--- a/railties/test/gem_dependency_test.rb
+++ b/railties/test/gem_dependency_test.rb
@@ -1,12 +1,11 @@
-require 'lib/rails/vendor_gem_source_index'
-Rails::VendorGemSourceIndex.silence_spec_warnings = true
-
require 'plugin_test_helper'
class Rails::GemDependency
public :install_command, :unpack_command
end
+Rails::VendorGemSourceIndex.silence_spec_warnings = true
+
uses_mocha "Plugin Tests" do
class GemDependencyTest < Test::Unit::TestCase
def setup