Skip to content
Browse files

Merge branch 'master' into feature/public-fragment_name_with_digest

  • Loading branch information...
2 parents 4cb50a3 + d7d2284 commit 1f3a8c873ed9b3fc8eb41ae775a570b95ebdf715 @rgarver rgarver committed
View
4 activemodel/lib/active_model/validations.rb
@@ -188,9 +188,9 @@ def validators
# # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={:in=>0..99}>
# # ]
def validators_on(*attributes)
- attributes.map do |attribute|
+ attributes.flat_map do |attribute|
_validators[attribute.to_sym]
- end.flatten
+ end
end
# Returns +true+ if +attribute+ is an attribute method, +false+ otherwise.
View
17 activerecord/CHANGELOG.md
@@ -1,5 +1,22 @@
## Rails 4.0.0 (unreleased) ##
+* `CollectionAssociation#count` returns `0` without querying if the
+ parent record is not persisted.
+
+ Before:
+
+ person.pets.count
+ # SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" IS NULL
+ # => 0
+
+ After:
+
+ person.pets.count
+ # fires without sql query
+ # => 0
+
+ *Francesco Rodriguez*
+
* Fix `reset_counters` crashing on `has_many :through` associations.
Fix #7822.
View
2 activerecord/lib/active_record/associations/collection_association.rb
@@ -174,6 +174,8 @@ def sum(*args)
# association, it will be used for the query. Otherwise, construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
+ return 0 if owner.new_record?
+
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
if options[:counter_sql] || options[:finder_sql]
View
9 activerecord/lib/active_record/counter_cache.rb
@@ -23,12 +23,11 @@ def reset_counters(id, *counters)
has_many_association = reflect_on_association(association.to_sym)
if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
- foreign_key = has_many_association.through_reflection.foreign_key.to_s
- child_class = has_many_association.through_reflection.klass
- else
- foreign_key = has_many_association.foreign_key.to_s
- child_class = has_many_association.klass
+ has_many_association = has_many_association.through_reflection
end
+
+ foreign_key = has_many_association.foreign_key.to_s
+ child_class = has_many_association.klass
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
counter_name = reflection.counter_cache_column
View
6 activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -799,6 +799,12 @@ def test_count_with_counter_sql
assert_equal 1, developer.projects.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Developer.new.projects.count
+ end
+ end
+
unless current_adapter?(:PostgreSQLAdapter)
def test_count_with_finder_sql
assert_equal 3, projects(:active_record).developers_with_finder_sql.count
View
6 activerecord/test/cases/associations/has_many_associations_test.rb
@@ -262,6 +262,12 @@ def test_counting_with_association_limit
assert_equal firm.limited_clients.length, firm.limited_clients.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.readers.count
+ end
+ end
+
def test_finding
assert_equal 2, Firm.all.merge!(:order => "id").first.clients.length
end
View
6 activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -767,6 +767,12 @@ def test_count_has_many_through_with_named_scope
assert_equal 1, authors(:mary).categories.general.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.posts.count
+ end
+ end
+
def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes
post = posts(:eager_other)
View
1 activesupport/lib/active_support.rb
@@ -48,6 +48,7 @@ module ActiveSupport
autoload :Gzip
autoload :Inflector
autoload :JSON
+ autoload :KeyGenerator
autoload :MessageEncryptor
autoload :MessageVerifier
autoload :Multibyte
View
23 activesupport/lib/active_support/key_generator.rb
@@ -0,0 +1,23 @@
+require 'openssl'
+
+module ActiveSupport
+ # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
+ # It can be used to derive a number of keys for various purposes from a given secret.
+ # This lets rails applications have a single secure secret, but avoid reusing that
+ # key in multiple incompatible contexts.
+ class KeyGenerator
+ def initialize(secret, options = {})
+ @secret = secret
+ # The default iterations are higher than required for our key derivation uses
+ # on the off chance someone uses this for password storage
+ @iterations = options[:iterations] || 2**16
+ end
+
+ # Returns a derived key suitable for use. The default key_size is chosen
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
+ # i.e. OpenSSL::Digest::SHA1#block_length
+ def generate_key(salt, key_size=64)
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
+ end
+ end
+end
View
32 activesupport/test/key_generator_test.rb
@@ -0,0 +1,32 @@
+require 'abstract_unit'
+
+begin
+ require 'openssl'
+ OpenSSL::PKCS5
+rescue LoadError, NameError
+ $stderr.puts "Skipping KeyGenerator test: broken OpenSSL install"
+else
+
+require 'active_support/time'
+require 'active_support/json'
+
+class KeyGeneratorTest < ActiveSupport::TestCase
+ def setup
+ @secret = SecureRandom.hex(64)
+ @generator = ActiveSupport::KeyGenerator.new(@secret, :iterations=>2)
+ end
+
+ test "Generating a key of the default length" do
+ derived_key = @generator.generate_key("some_salt")
+ assert_kind_of String, derived_key
+ assert_equal OpenSSL::Digest::SHA1.new.block_length, derived_key.length, "Should have generated a key of the default size"
+ end
+
+ test "Generating a key of an alternative length" do
+ derived_key = @generator.generate_key("some_salt", 32)
+ assert_kind_of String, derived_key
+ assert_equal 32, derived_key.length, "Should have generated a key of the right size"
+ end
+end
+
+end
View
2 railties/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* `Rails.public_path` now returns a Pathname object. *Prem Sichanugrist*
+
* Remove highly uncommon `config.assets.manifest` option for moving the manifest path.
This option is now unsupported in sprockets-rails.
View
2 railties/lib/rails.rb
@@ -127,7 +127,7 @@ def version
end
def public_path
- application && application.paths["public"].first
+ application && Pathname.new(application.paths["public"].first)
end
end
end
View
11 railties/lib/rails/application.rb
@@ -101,6 +101,14 @@ def reload_routes!
routes_reloader.reload!
end
+
+ # Return the application's KeyGenerator
+ def key_generator
+ # number of iterations selected based on consultation with the google security
+ # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
+ @key_generator ||= ActiveSupport::KeyGenerator.new(config.secret_token, :iterations=>1000)
+ end
+
# Stores some of the Rails initial environment parameters which
# will be used by middlewares and engines to configure themselves.
# Currently stores:
@@ -121,7 +129,8 @@ def env_config
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
"action_dispatch.logger" => Rails.logger,
- "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
+ "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
+ "action_dispatch.key_generator" => key_generator
})
end
View
11 railties/test/application/configuration_test.rb
@@ -139,6 +139,14 @@ def teardown
assert_instance_of Pathname, Rails.root
end
+ test "Rails.public_path should be a Pathname" do
+ add_to_config <<-RUBY
+ config.paths["public"] = "somewhere"
+ RUBY
+ require "#{app_path}/config/environment"
+ assert_instance_of Pathname, Rails.public_path
+ end
+
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true
@@ -227,7 +235,7 @@ def assert_utf8
RUBY
require "#{app_path}/config/application"
- assert_equal File.join(app_path, "somewhere"), Rails.public_path
+ assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path
end
test "config.secret_token is sent in env" do
@@ -634,6 +642,7 @@ def index
assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions
assert_equal app.env_config['action_dispatch.logger'], Rails.logger
assert_equal app.env_config['action_dispatch.backtrace_cleaner'], Rails.backtrace_cleaner
+ assert_equal app.env_config['action_dispatch.key_generator'], Rails.application.key_generator
end
test "config.colorize_logging default is true" do

0 comments on commit 1f3a8c8

Please sign in to comment.
Something went wrong with that request. Please try again.