aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile3
-rwxr-xr-xRakefile14
-rw-r--r--actionpack/lib/action_controller/metal.rb8
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb8
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb19
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb89
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb2
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb6
-rw-r--r--actionpack/test/template/text_helper_test.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb27
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb5
-rw-r--r--activerecord/test/cases/migration_test.rb69
-rw-r--r--activesupport/CHANGELOG.md2
-rw-r--r--activesupport/lib/active_support/callbacks.rb55
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb60
-rw-r--r--activesupport/test/message_encryptor_test.rb59
-rw-r--r--railties/guides/code/getting_started/app/controllers/posts_controller.rb4
-rw-r--r--railties/guides/rails_guides/generator.rb3
-rw-r--r--railties/guides/source/getting_started.textile4
-rw-r--r--railties/lib/rails/commands/console.rb2
-rw-r--r--railties/lib/rails/console/app.rb48
-rw-r--r--railties/lib/rails/console/helpers.rb14
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/gitignore6
-rw-r--r--railties/lib/rails/tasks/documentation.rake4
-rw-r--r--railties/lib/rails/test_unit/testing.rake13
-rw-r--r--railties/test/application/console_test.rb14
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb8
31 files changed, 356 insertions, 206 deletions
diff --git a/Gemfile b/Gemfile
index f94f267f05..1b7d478f18 100644
--- a/Gemfile
+++ b/Gemfile
@@ -41,8 +41,7 @@ platforms :mri_18 do
end
platforms :mri_19 do
- # TODO: Remove the conditional when ruby-debug19 supports Ruby >= 1.9.3
- gem "ruby-debug19", :require => "ruby-debug" unless RUBY_VERSION > "1.9.2" || ENV['TRAVIS']
+ gem "ruby-debug19", :require => "ruby-debug" unless ENV['TRAVIS']
end
platforms :mri do
diff --git a/Rakefile b/Rakefile
index a47d415abc..e59abd5758 100755
--- a/Rakefile
+++ b/Rakefile
@@ -97,24 +97,24 @@ RDoc::Task.new do |rdoc|
rdoc.options << '-g' # SDoc flag, link methods to GitHub
rdoc.options << '-m' << RDOC_MAIN
- rdoc.rdoc_files.include('railties/CHANGELOG')
+ rdoc.rdoc_files.include('railties/CHANGELOG.md')
rdoc.rdoc_files.include('railties/MIT-LICENSE')
rdoc.rdoc_files.include('railties/README.rdoc')
rdoc.rdoc_files.include('railties/lib/**/*.rb')
rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/**/*.rb')
rdoc.rdoc_files.include('activerecord/README.rdoc')
- rdoc.rdoc_files.include('activerecord/CHANGELOG')
+ rdoc.rdoc_files.include('activerecord/CHANGELOG.md')
rdoc.rdoc_files.include('activerecord/lib/active_record/**/*.rb')
rdoc.rdoc_files.exclude('activerecord/lib/active_record/vendor/*')
rdoc.rdoc_files.include('activeresource/README.rdoc')
- rdoc.rdoc_files.include('activeresource/CHANGELOG')
+ rdoc.rdoc_files.include('activeresource/CHANGELOG.md')
rdoc.rdoc_files.include('activeresource/lib/active_resource.rb')
rdoc.rdoc_files.include('activeresource/lib/active_resource/*')
rdoc.rdoc_files.include('actionpack/README.rdoc')
- rdoc.rdoc_files.include('actionpack/CHANGELOG')
+ rdoc.rdoc_files.include('actionpack/CHANGELOG.md')
rdoc.rdoc_files.include('actionpack/lib/abstract_controller/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
@@ -122,18 +122,18 @@ RDoc::Task.new do |rdoc|
rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*')
rdoc.rdoc_files.include('actionmailer/README.rdoc')
- rdoc.rdoc_files.include('actionmailer/CHANGELOG')
+ rdoc.rdoc_files.include('actionmailer/CHANGELOG.md')
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb')
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/mail_helper.rb')
rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*')
rdoc.rdoc_files.include('activesupport/README.rdoc')
- rdoc.rdoc_files.include('activesupport/CHANGELOG')
+ rdoc.rdoc_files.include('activesupport/CHANGELOG.md')
rdoc.rdoc_files.include('activesupport/lib/active_support/**/*.rb')
rdoc.rdoc_files.exclude('activesupport/lib/active_support/vendor/*')
rdoc.rdoc_files.include('activemodel/README.rdoc')
- rdoc.rdoc_files.include('activemodel/CHANGELOG')
+ rdoc.rdoc_files.include('activemodel/CHANGELOG.md')
rdoc.rdoc_files.include('activemodel/lib/active_model/**/*.rb')
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 0133b2ecbc..125dbf6bb5 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -182,7 +182,13 @@ module ActionController
end
def response_body=(val)
- body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
+ body = if val.is_a?(String)
+ [val]
+ elsif val.nil? || val.respond_to?(:each)
+ val
+ else
+ [val]
+ end
super body
end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index b932302a60..9500a349cb 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -84,8 +84,8 @@ module ActionController #:nodoc:
#
# === Custom options
#
- # <code>respond_with</code> also allow you to pass options that are forwarded
- # to the underlying render call. Those options are only applied success
+ # <code>respond_with</code> also allows you to pass options that are forwarded
+ # to the underlying render call. Those options are only applied for success
# scenarios. For instance, you can do the following in the create method above:
#
# def create
@@ -95,7 +95,7 @@ module ActionController #:nodoc:
# respond_with(@project, @task, :status => 201)
# end
#
- # This will return status 201 if the task was saved with success. If not,
+ # This will return status 201 if the task was saved successfully. If not,
# it will simply ignore the given options and return status 422 and the
# resource errors. To customize the failure scenario, you can pass a
# a block to <code>respond_with</code>:
@@ -222,7 +222,7 @@ module ActionController #:nodoc:
alias :navigation_location :resource_location
alias :api_location :resource_location
- # If a given response block was given, use it, otherwise call render on
+ # If a response block was given, use it, otherwise call render on
# controller.
#
def default_render
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index 505d5560b1..040b51e040 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -1,5 +1,3 @@
-require 'active_support/memoizable'
-
module ActionDispatch
module Http
class Headers < ::Hash
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 7a5237dcf3..69ca050d0c 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -155,24 +155,7 @@ module ActionDispatch
@ip ||= super
end
- # Which IP addresses are "trusted proxies" that can be stripped from
- # the right-hand-side of X-Forwarded-For.
- #
- # http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces.
- TRUSTED_PROXIES = %r{
- ^127\.0\.0\.1$ | # localhost
- ^(10 | # private IP 10.x.x.x
- 172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
- 192\.168 # private IP 192.168.x.x
- )\.
- }x
-
- # Determines originating IP address. REMOTE_ADDR is the standard
- # but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
- # HTTP_X_FORWARDED_FOR are set by proxies so check for these if
- # REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
- # delimited list in the case of multiple chained proxies; the last
- # address which is not trusted is the originating IP.
+ # Originating IP address, usually set by the RemoteIp middleware.
def remote_ip
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index c7d710b98e..3b813b03bb 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -2,50 +2,69 @@ module ActionDispatch
class RemoteIp
class IpSpoofAttackError < StandardError ; end
- class RemoteIpGetter
- def initialize(env, check_ip_spoofing, trusted_proxies)
- @env = env
- @check_ip_spoofing = check_ip_spoofing
- @trusted_proxies = trusted_proxies
+ # IP addresses that are "trusted proxies" that can be stripped from
+ # the comma-delimited list in the X-Forwarded-For header. See also:
+ # http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
+ TRUSTED_PROXIES = %r{
+ ^127\.0\.0\.1$ | # localhost
+ ^(10 | # private IP 10.x.x.x
+ 172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
+ 192\.168 # private IP 192.168.x.x
+ )\.
+ }x
+
+ attr_reader :check_ip_spoofing, :trusted_proxies
+
+ def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
+ @app = app
+ @check_ip_spoofing = check_ip_spoofing
+ if custom_proxies
+ custom_regexp = Regexp.new(custom_proxies, "i")
+ @trusted_proxies = Regexp.union(TRUSTED_PROXIES, custom_regexp)
+ else
+ @trusted_proxies = TRUSTED_PROXIES
end
+ end
- def remote_addrs
- @remote_addrs ||= begin
- list = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
- list.reject { |addr| addr =~ @trusted_proxies }
- end
+ def call(env)
+ env["action_dispatch.remote_ip"] = GetIp.new(env, self)
+ @app.call(env)
+ end
+
+ class GetIp
+ def initialize(env, middleware)
+ @env, @middleware = env, middleware
end
+ # Determines originating IP address. REMOTE_ADDR is the standard
+ # but will be wrong if the user is behind a proxy. Proxies will set
+ # HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR, so we prioritize those.
+ # HTTP_X_FORWARDED_FOR may be a comma-delimited list in the case of
+ # multiple chained proxies. The last address which is not a known proxy
+ # will be the originating IP.
def to_s
- return remote_addrs.first if remote_addrs.any?
-
- forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
-
- if client_ip = @env['HTTP_CLIENT_IP']
- if @check_ip_spoofing && !forwarded_ips.include?(client_ip)
- # We don't know which came from the proxy, and which from the user
- raise IpSpoofAttackError, "IP spoofing attack?!" \
- "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
- "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
- end
- return client_ip
+ client_ip = @env['HTTP_CLIENT_IP']
+ forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR')
+ remote_addrs = ips_from('REMOTE_ADDR')
+
+ check_ip = client_ip && @middleware.check_ip_spoofing
+ if check_ip && !forwarded_ips.include?(client_ip)
+ # We don't know which came from the proxy, and which from the user
+ raise IpSpoofAttackError, "IP spoofing attack?!" \
+ "HTTP_CLIENT_IP=#{env['HTTP_CLIENT_IP'].inspect}" \
+ "HTTP_X_FORWARDED_FOR=#{env['HTTP_X_FORWARDED_FOR'].inspect}"
end
- return forwarded_ips.reject { |ip| ip =~ @trusted_proxies }.last || @env["REMOTE_ADDR"]
+ client_ip || forwarded_ips.last || remote_addrs.last
end
- end
- def initialize(app, check_ip_spoofing = true, trusted_proxies = nil)
- @app = app
- @check_ip_spoofing = check_ip_spoofing
- regex = '(^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.)'
- regex << "|(#{trusted_proxies})" if trusted_proxies
- @trusted_proxies = Regexp.new(regex, "i")
- end
+ protected
- def call(env)
- env["action_dispatch.remote_ip"] = RemoteIpGetter.new(env, @check_ip_spoofing, @trusted_proxies)
- @app.call(env)
+ def ips_from(header)
+ ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
+ ips.reject{|ip| ip =~ @middleware.trusted_proxies }
+ end
end
+
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 21074efe86..bc8572fe69 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -120,7 +120,7 @@ module ActionView
text
else
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
- text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
+ text.gsub(/(#{match})(?![^<]*?>)/i, options[:highlighter])
end.html_safe
end
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index 3ca29f1bcf..c424dcbd8d 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -23,6 +23,12 @@ module BareMetalTest
assert_equal "Hello world", string
end
+
+ test "response_body value is wrapped in an array when the value is a String" do
+ controller = BareController.new
+ controller.index
+ assert_equal ["Hello world"], controller.response_body
+ end
end
class HeadController < ActionController::Metal
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 02f9609483..a0afb77f05 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -194,6 +194,10 @@ class TextHelperTest < ActionView::TestCase
"<p>This is a <strong class=\"highlight\">beautiful</strong> <a href=\"http://example.com/beautiful#top?what=beautiful%20morning&amp;when=now+then\">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
highlight("<p>This is a beautiful <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&when=now+then\">morning</a>, but also a beautiful day</p>", "beautiful")
)
+ assert_equal(
+ "<div>abc <b>div</b></div>",
+ highlight("<div>abc div</div>", "div", :highlighter => '<b>\1</b>')
+ )
end
def test_excerpt
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 77a5fe1efb..92dfb844db 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -123,14 +123,14 @@ module ActiveRecord
# A cached lookup for table existence.
def table_exists?(name)
- return true if @tables.key? name
+ return @tables[name] if @tables.key? name
with_connection do |conn|
conn.tables.each { |table| @tables[table] = true }
- @tables[name] = true if !@tables.key?(name) && conn.table_exists?(name)
+ @tables[name] = !@tables.key?(name) && conn.table_exists?(name)
end
- @tables.key? name
+ @tables[name]
end
# Clears out internal caches:
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 0e5e33fa02..11da84e245 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -158,7 +158,13 @@ module ActiveRecord
td = table_definition
td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
- td.instance_eval(&blk) if blk
+ if block_given?
+ if blk.arity == 1
+ yield td
+ else
+ td.instance_eval(&blk)
+ end
+ end
if options[:force] && table_exists?(table_name)
drop_table(table_name)
@@ -235,14 +241,19 @@ module ActiveRecord
#
# See also Table for details on
# all of the various column transformation
- def change_table(table_name, options = {})
- if supports_bulk_alter? && options[:bulk]
- recorder = ActiveRecord::Migration::CommandRecorder.new(self)
- yield Table.new(table_name, recorder)
- bulk_change_table(table_name, recorder.commands)
- else
- yield Table.new(table_name, self)
+ def change_table(table_name, options = {}, &blk)
+ bulk_change = supports_bulk_alter? && options[:bulk]
+ recorder = bulk_change ? ActiveRecord::Migration::CommandRecorder.new(self) : self
+ table = Table.new(table_name, recorder)
+
+ if block_given?
+ if blk.arity == 1
+ yield table
+ else
+ table.instance_eval(&blk)
+ end
end
+ bulk_change_table(table_name, recorder.commands) if bulk_change
end
# Renames a table.
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 7eeb3dde70..3c8e0f2052 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -191,7 +191,7 @@ module ActiveRecord
join_dependency = construct_join_dependency_for_association_find
relation = construct_relation_for_association_find(join_dependency)
- relation = relation.except(:select).select("1").limit(1)
+ relation = relation.except(:select, :order).select("1").limit(1)
case id
when Array, Hash
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 3088ab012f..69754d23b9 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -57,6 +57,11 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.first.replies.exists?
end
+ # ensures +exists?+ runs valid SQL by excluding order value
+ def test_exists_with_order
+ assert Topic.order(:id).uniq.exists?
+ end
+
def test_does_not_exist_with_empty_table_and_no_args_given
Topic.delete_all
assert !Topic.exists?
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index e8ad37d437..d6c7edc461 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1742,6 +1742,75 @@ if ActiveRecord::Base.connection.supports_migrations?
ensure
Person.connection.drop_table :testings rescue nil
end
+
+ def test_create_table_should_not_have_mixed_syntax
+ assert_raise(NoMethodError) do
+ Person.connection.create_table :testings, :force => true do |t|
+ t.string :foo
+ integer :bar
+ end
+ end
+ assert_raise(NameError) do
+ Person.connection.create_table :testings, :force => true do
+ t.string :foo
+ integer :bar
+ end
+ end
+ end
+
+ def test_change_table_without_block_parameter_no_bulk
+ Person.connection.create_table :testings, :force => true do
+ string :foo
+ end
+ assert Person.connection.column_exists?(:testings, :foo, :string)
+
+ Person.connection.change_table :testings do
+ remove :foo
+ integer :bar
+ end
+
+ assert_equal %w(bar id), Person.connection.columns(:testings).map { |c| c.name }.sort
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
+ if ActiveRecord::Base.connection.supports_bulk_alter?
+ def test_change_table_without_block_parameter_with_bulk
+ Person.connection.create_table :testings, :force => true do
+ string :foo
+ end
+ assert Person.connection.column_exists?(:testings, :foo, :string)
+
+ assert_queries(1) do
+ Person.connection.change_table(:testings, :bulk => true) do
+ integer :bar
+ string :foo_bar
+ end
+ end
+
+ assert_equal %w(bar foo foo_bar id), Person.connection.columns(:testings).map { |c| c.name }.sort
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+ end
+
+ def test_change_table_should_not_have_mixed_syntax
+ Person.connection.create_table :testings, :force => true do
+ string :foo
+ end
+ assert_raise(NoMethodError) do
+ Person.connection.change_table :testings do |t|
+ t.remove :foo
+ integer :bar
+ end
+ end
+ assert_raise(NameError) do
+ Person.connection.change_table :testings do
+ t.remove :foo
+ integer :bar
+ end
+ end
+ end
end # SexierMigrationsTest
class MigrationLoggerTest < ActiveRecord::TestCase
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index e03bfdee92..18a115b369 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 3.2.0 (unreleased) ##
+* Deprecated ActiveSupport::MessageEncryptor#encrypt and decrypt. *José Valim*
+
* ActiveSupport::Notifications.subscribed provides subscriptions to events while a block runs. *fxn*
* Module#qualified_const_(defined?|get|set) are analogous to the corresponding methods
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 656cba625c..ea37355fc1 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -153,7 +153,7 @@ module ActiveSupport
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def _one_time_conditions_valid_#{@callback_id}?
- true #{key_options[0]}
+ true if #{key_options}
end
RUBY_EVAL
end
@@ -171,8 +171,8 @@ module ActiveSupport
# if condition # before_save :filter_name, :if => :condition
# filter_name
# end
- filter = <<-RUBY_EVAL
- unless halted
+ <<-RUBY_EVAL
+ if !halted && #{@compiled_options}
# This double assignment is to prevent warnings in 1.9.3. I would
# remove the `result` variable, but apparently some other
# generated code is depending on this variable being set sometimes
@@ -181,8 +181,6 @@ module ActiveSupport
halted = (#{chain.config[:terminator]})
end
RUBY_EVAL
-
- [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
when :around
# Compile around filters with conditions into proxy methods
# that contain the conditions.
@@ -202,7 +200,7 @@ module ActiveSupport
name = "_conditional_callback_#{@kind}_#{next_id}"
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}(halted)
- #{@compiled_options[0] || "if true"} && !halted
+ if #{@compiled_options} && !halted
#{@filter} do
yield self
end
@@ -222,10 +220,12 @@ module ActiveSupport
case @kind
when :after
- # if condition # after_save :filter_name, :if => :condition
- # filter_name
- # end
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
+ # after_save :filter_name, :if => :condition
+ <<-RUBY_EVAL
+ if #{@compiled_options}
+ #{@filter}
+ end
+ RUBY_EVAL
when :around
<<-RUBY_EVAL
value
@@ -240,9 +240,7 @@ module ActiveSupport
# symbols, string, procs, and objects), so compile a conditional
# expression based on the options
def _compile_options(options)
- return [] if options[:if].empty? && options[:unless].empty?
-
- conditions = []
+ conditions = ["true"]
unless options[:if].empty?
conditions << Array.wrap(_compile_filter(options[:if]))
@@ -252,7 +250,7 @@ module ActiveSupport
conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
end
- ["if #{conditions.flatten.join(" && ")}", "end"]
+ conditions.flatten.join(" && ")
end
# Filters support:
@@ -371,42 +369,37 @@ module ActiveSupport
# Generate the internal runner method called by +run_callbacks+.
def __define_runner(symbol) #:nodoc:
body = send("_#{symbol}_callbacks").compile
+ runner_method = "_run_#{symbol}_callbacks"
silence_warnings do
- undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
+ undef_method runner_method if method_defined?(runner_method)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _run_#{symbol}_callbacks(key = nil, &blk)
+ def #{runner_method}(key = nil, &blk)
if key
- name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
-
- unless respond_to?(name)
- self.class.__create_keyed_callback(name, :#{symbol}, self, &blk)
- end
-
- send(name, &blk)
+ self.class.__run_keyed_callback(key, :#{symbol}, self, &blk)
else
#{body}
end
end
- private :_run_#{symbol}_callbacks
+ private :#{runner_method}
RUBY_EVAL
end
end
- # This is called the first time a callback is called with a particular
- # key. It creates a new callback method for the key, calculating
- # which callbacks can be omitted because of per_key conditions.
+ # This method calls the callback method for the given key.
+ # If this called first time it creates a new callback method for the key,
+ # calculating which callbacks can be omitted because of per_key conditions.
#
- def __create_keyed_callback(name, kind, object, &blk) #:nodoc:
- @_keyed_callbacks ||= {}
- @_keyed_callbacks[name] ||= begin
+ def __run_keyed_callback(key, kind, object, &blk) #:nodoc:
+ name = "_run__#{self.name.hash.abs}__#{kind}__#{key.hash.abs}__callbacks"
+ unless object.respond_to?(name)
str = send("_#{kind}_callbacks").compile(name, object)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}() #{str} end
protected :#{name}
RUBY_EVAL
- true
end
+ object.send(name, &blk)
end
# This is used internally to append, prepend and skip callbacks to the
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index e14386a85d..9ef2b29580 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -10,6 +10,16 @@ module ActiveSupport
# This can be used in situations similar to the <tt>MessageVerifier</tt>, but where you don't
# want users to be able to determine the value of the payload.
class MessageEncryptor
+ module NullSerializer #:nodoc:
+ def self.load(value)
+ value
+ end
+
+ def self.dump(value)
+ value
+ end
+ end
+
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
@@ -18,13 +28,40 @@ module ActiveSupport
ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :cipher => 'algorithm' to specify the cipher algorithm."
options = { :cipher => options }
end
-
+
@secret = secret
@cipher = options[:cipher] || 'aes-256-cbc'
+ @verifier = MessageVerifier.new(@secret, :serializer => NullSerializer)
@serializer = options[:serializer] || Marshal
end
def encrypt(value)
+ ActiveSupport::Deprecation.warn "MessageEncryptor#encrypt is deprecated as it is not safe without a signature. " \
+ "Please use MessageEncryptor#encrypt_and_sign instead."
+ _encrypt(value)
+ end
+
+ def decrypt(value)
+ ActiveSupport::Deprecation.warn "MessageEncryptor#decrypt is deprecated as it is not safe without a signature. " \
+ "Please use MessageEncryptor#decrypt_and_verify instead."
+ _decrypt(value)
+ end
+
+ # Encrypt and sign a message. We need to sign the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def encrypt_and_sign(value)
+ verifier.generate(_encrypt(value))
+ end
+
+ # Decrypt and verify a message. We need to verify the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def decrypt_and_verify(value)
+ _decrypt(verifier.verify(value))
+ end
+
+ private
+
+ def _encrypt(value)
cipher = new_cipher
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
@@ -39,7 +76,7 @@ module ActiveSupport
[encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
end
- def decrypt(encrypted_message)
+ def _decrypt(encrypted_message)
cipher = new_cipher
encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
@@ -55,23 +92,12 @@ module ActiveSupport
raise InvalidMessage
end
- def encrypt_and_sign(value)
- verifier.generate(encrypt(value))
+ def new_cipher
+ OpenSSL::Cipher::Cipher.new(@cipher)
end
- def decrypt_and_verify(value)
- decrypt(verifier.verify(value))
+ def verifier
+ @verifier
end
-
-
-
- private
- def new_cipher
- OpenSSL::Cipher::Cipher.new(@cipher)
- end
-
- def verifier
- MessageVerifier.new(@secret)
- end
end
end
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index 83a19f8106..3e6a5c6602 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -11,46 +11,50 @@ require 'active_support/time'
require 'active_support/json'
class MessageEncryptorTest < ActiveSupport::TestCase
-
class JSONSerializer
def dump(value)
ActiveSupport::JSON.encode(value)
end
-
+
def load(value)
ActiveSupport::JSON.decode(value)
end
end
-
+
def setup
- @encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64))
+ @secret = SecureRandom.hex(64)
+ @verifier = ActiveSupport::MessageVerifier.new(@secret, :serializer => ActiveSupport::MessageEncryptor::NullSerializer)
+ @encryptor = ActiveSupport::MessageEncryptor.new(@secret)
@data = { :some => "data", :now => Time.local(2010) }
end
- def test_simple_round_tripping
- message = @encryptor.encrypt(@data)
- assert_equal @data, @encryptor.decrypt(message)
- end
-
def test_encrypting_twice_yields_differing_cipher_text
- first_messqage = @encryptor.encrypt(@data)
- second_message = @encryptor.encrypt(@data)
+ first_messqage = @encryptor.encrypt_and_sign(@data).split("--").first
+ second_message = @encryptor.encrypt_and_sign(@data).split("--").first
assert_not_equal first_messqage, second_message
end
- def test_messing_with_either_value_causes_failure
- text, iv = @encryptor.encrypt(@data).split("--")
+ def test_messing_with_either_encrypted_values_causes_failure
+ text, iv = @verifier.verify(@encryptor.encrypt_and_sign(@data)).split("--")
assert_not_decrypted([iv, text] * "--")
assert_not_decrypted([text, munge(iv)] * "--")
assert_not_decrypted([munge(text), iv] * "--")
assert_not_decrypted([munge(text), munge(iv)] * "--")
end
+ def test_messing_with_verified_values_causes_failures
+ text, iv = @encryptor.encrypt_and_sign(@data).split("--")
+ assert_not_verified([iv, text] * "--")
+ assert_not_verified([text, munge(iv)] * "--")
+ assert_not_verified([munge(text), iv] * "--")
+ assert_not_verified([munge(text), munge(iv)] * "--")
+ end
+
def test_signed_round_tripping
message = @encryptor.encrypt_and_sign(@data)
assert_equal @data, @encryptor.decrypt_and_verify(message)
end
-
+
def test_alternative_serialization_method
encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), :serializer => JSONSerializer.new)
message = encryptor.encrypt_and_sign({ :foo => 123, 'bar' => Time.utc(2010) })
@@ -62,19 +66,26 @@ class MessageEncryptorTest < ActiveSupport::TestCase
ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), 'aes-256-cbc')
end
end
-
+
private
- def assert_not_decrypted(value)
- assert_raise(ActiveSupport::MessageEncryptor::InvalidMessage) do
- @encryptor.decrypt(value)
- end
+
+ def assert_not_decrypted(value)
+ assert_raise(ActiveSupport::MessageEncryptor::InvalidMessage) do
+ @encryptor.decrypt_and_verify(@verifier.generate(value))
end
+ end
- def munge(base64_string)
- bits = ActiveSupport::Base64.decode64(base64_string)
- bits.reverse!
- ActiveSupport::Base64.encode64s(bits)
+ def assert_not_verified(value)
+ assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
+ @encryptor.decrypt_and_verify(value)
end
-end
+ end
+ def munge(base64_string)
+ bits = ActiveSupport::Base64.decode64(base64_string)
+ bits.reverse!
+ ActiveSupport::Base64.encode64s(bits)
+ end
end
+
+end \ No newline at end of file
diff --git a/railties/guides/code/getting_started/app/controllers/posts_controller.rb b/railties/guides/code/getting_started/app/controllers/posts_controller.rb
index 7e903c984c..1581d4eb16 100644
--- a/railties/guides/code/getting_started/app/controllers/posts_controller.rb
+++ b/railties/guides/code/getting_started/app/controllers/posts_controller.rb
@@ -62,7 +62,7 @@ class PostsController < ApplicationController
respond_to do |format|
if @post.update_attributes(params[:post])
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
- format.json { head :ok }
+ format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @post.errors, status: :unprocessable_entity }
@@ -78,7 +78,7 @@ class PostsController < ApplicationController
respond_to do |format|
format.html { redirect_to posts_url }
- format.json { head :ok }
+ format.json { head :no_content }
end
end
end
diff --git a/railties/guides/rails_guides/generator.rb b/railties/guides/rails_guides/generator.rb
index 4682ead66e..6f6d3bda80 100644
--- a/railties/guides/rails_guides/generator.rb
+++ b/railties/guides/rails_guides/generator.rb
@@ -137,7 +137,8 @@ module RailsGuides
if guide =~ /\.html\.erb$/
# Generate the special pages like the home.
- result = view.render(:layout => 'layout', :file => guide)
+ # Passing a template handler in the template name is deprecated. So pass the file name without the extension.
+ result = view.render(:layout => 'layout', :file => $`)
else
body = File.read(File.join(source_dir, guide))
body = set_header_section(body, view)
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 5f370615ca..8a0a70efad 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -1071,7 +1071,7 @@ def update
if @post.update_attributes(params[:post])
format.html { redirect_to(@post,
:notice => 'Post was successfully updated.') }
- format.json { render :json => {}, :status => :ok }
+ format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => @post.errors,
@@ -1100,7 +1100,7 @@ def destroy
respond_to do |format|
format.html { redirect_to posts_url }
- format.json { head :ok }
+ format.json { head :no_content }
end
end
</ruby>
diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb
index 32e361d421..7733a8f116 100644
--- a/railties/lib/rails/commands/console.rb
+++ b/railties/lib/rails/commands/console.rb
@@ -42,6 +42,8 @@ module Rails
else
puts "Loading #{Rails.env} environment (Rails #{Rails.version})"
end
+
+ IRB::ExtendCommandBundle.send :include, Rails::ConsoleMethods
IRB.start
end
end
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
index 95c74baae2..23d57379ba 100644
--- a/railties/lib/rails/console/app.rb
+++ b/railties/lib/rails/console/app.rb
@@ -5,28 +5,32 @@ require 'action_controller'
# work around the at_exit hook in test/unit, which kills IRB
Test::Unit.run = true if Test::Unit.respond_to?(:run=)
-# reference the global "app" instance, created on demand. To recreate the
-# instance, pass a non-false value as the parameter.
-def app(create=false)
- @app_integration_instance = nil if create
- @app_integration_instance ||= new_session do |sess|
- sess.host! "www.example.com"
- end
-end
+module Rails
+ module ConsoleMethods
+ # reference the global "app" instance, created on demand. To recreate the
+ # instance, pass a non-false value as the parameter.
+ def app(create=false)
+ @app_integration_instance = nil if create
+ @app_integration_instance ||= new_session do |sess|
+ sess.host! "www.example.com"
+ end
+ end
-# create a new session. If a block is given, the new session will be yielded
-# to the block before being returned.
-def new_session
- app = Rails.application
- session = ActionDispatch::Integration::Session.new(app)
- yield session if block_given?
- session
-end
+ # create a new session. If a block is given, the new session will be yielded
+ # to the block before being returned.
+ def new_session
+ app = Rails.application
+ session = ActionDispatch::Integration::Session.new(app)
+ yield session if block_given?
+ session
+ end
-# reloads the environment
-def reload!(print=true)
- puts "Reloading..." if print
- ActionDispatch::Reloader.cleanup!
- ActionDispatch::Reloader.prepare!
- true
+ # reloads the environment
+ def reload!(print=true)
+ puts "Reloading..." if print
+ ActionDispatch::Reloader.cleanup!
+ ActionDispatch::Reloader.prepare!
+ true
+ end
+ end
end
diff --git a/railties/lib/rails/console/helpers.rb b/railties/lib/rails/console/helpers.rb
index 212fc6125a..230d3d9d04 100644
--- a/railties/lib/rails/console/helpers.rb
+++ b/railties/lib/rails/console/helpers.rb
@@ -1,7 +1,11 @@
-def helper
- @helper ||= ApplicationController.helpers
-end
+module Rails
+ module ConsoleMethods
+ def helper
+ @helper ||= ApplicationController.helpers
+ end
-def controller
- @controller ||= ApplicationController.new
+ def controller
+ @controller ||= ApplicationController.new
+ end
+ end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index 4baa2110e7..f5c8ccf940 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -39,7 +39,7 @@ module Rails
end
def gitignore
- copy_file "gitignore", ".gitignore"
+ template "gitignore", ".gitignore"
end
def lib
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
index 1463de6dfb..92bd3c614b 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
@@ -1,6 +1,6 @@
.bundle/
log/*.log
pkg/
-test/dummy/db/*.sqlite3
-test/dummy/log/*.log
-test/dummy/tmp/ \ No newline at end of file
+<%= dummy_path %>/db/*.sqlite3
+<%= dummy_path %>/log/*.log
+<%= dummy_path %>/tmp/ \ No newline at end of file
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index 2072a2b947..1e7da5ccae 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -96,13 +96,13 @@ namespace :doc do
end
gem_path('activesupport') do |activesupport|
- %w(README.rdoc CHANGELOG lib/active_support/**/*.rb).each do |file|
+ %w(README.rdoc CHANGELOG.md lib/active_support/**/*.rb).each do |file|
rdoc.rdoc_files.include("#{activesupport}/#{file}")
end
end
gem_path('railties') do |railties|
- %w(README.rdoc CHANGELOG lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file|
+ %w(README.rdoc CHANGELOG.md lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file|
rdoc.rdoc_files.include("#{railties}/#{file}")
end
end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 8bcceb9b2c..52d92cdd96 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -121,16 +121,9 @@ namespace :test do
models = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb$/ }
controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb$/ }
- unit_tests = models.map do |model|
- file = "test/unit/#{File.basename(model, '.rb')}_test.rb"
- file if File.exist?(file)
- end
- functional_tests = controllers.map do |controller|
- file = "test/functional/#{File.basename(controller, '.rb')}_test.rb"
- file if File.exist?(file)
- end
-
- (unit_tests.uniq + functional_tests.uniq).compact
+ unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" }
+ functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
+ (unit_tests + functional_tests).uniq.select { |file| File.exist?(file) }
end
t.libs << 'test'
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 1528d5dd87..b3745f194e 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -18,16 +18,20 @@ class ConsoleTest < Test::Unit::TestCase
Rails.application.load_console
end
+ def irb_context
+ Object.new.extend(Rails::ConsoleMethods)
+ end
+
def test_app_method_should_return_integration_session
TestHelpers::Rack.send :remove_method, :app
load_environment
- console_session = app
+ console_session = irb_context.app
assert_instance_of ActionDispatch::Integration::Session, console_session
end
def test_new_session_should_return_integration_session
load_environment
- session = new_session
+ session = irb_context.new_session
assert_instance_of ActionDispatch::Integration::Session, session
end
@@ -41,7 +45,7 @@ class ConsoleTest < Test::Unit::TestCase
ActionDispatch::Reloader.to_prepare { c = 3 }
# Hide Reloading... output
- silence_stream(STDOUT) { reload! }
+ silence_stream(STDOUT) { irb_context.reload! }
assert_equal 1, a
assert_equal 2, b
@@ -66,12 +70,14 @@ class ConsoleTest < Test::Unit::TestCase
MODEL
assert !User.new.respond_to?(:age)
- silence_stream(STDOUT) { reload! }
+ silence_stream(STDOUT) { irb_context.reload! }
+ session = irb_context.new_session
assert User.new.respond_to?(:age)
end
def test_access_to_helpers
load_environment
+ helper = irb_context.helper
assert_not_nil helper
assert_instance_of ActionView::Base, helper
assert_equal 'Once upon a time in a world...',
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 9183945619..b70f9f4d9c 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -236,6 +236,14 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_file "spec/dummy/config/application.rb"
assert_no_file "test"
end
+
+ def test_ensure_that_gitignore_can_be_generated_from_a_template_for_dummy_path
+ FileUtils.cd(Rails.root)
+ run_generator([destination_root, "--dummy_path", "spec/dummy" "--skip-test-unit"])
+ assert_file ".gitignore" do |contents|
+ assert_match(/spec\/dummy/, contents)
+ end
+ end
def test_skipping_test_unit
run_generator [destination_root, "--skip-test-unit"]