diff options
8 files changed, 66 insertions, 25 deletions
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index 2beb3f8365..c7671a8c22 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -17,7 +17,7 @@ module Remembered module ClassMethods def remembered; @@remembered ||= []; end - def random_element; @@remembered.random_element; end + def sample; @@remembered.sample; end end end @@ -79,14 +79,14 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase [Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!) end 1.upto(NUM_SIMPLE_OBJS) do - PaintColor.create!(:non_poly_one_id => NonPolyOne.random_element.id) - PaintTexture.create!(:non_poly_two_id => NonPolyTwo.random_element.id) + PaintColor.create!(:non_poly_one_id => NonPolyOne.sample.id) + PaintTexture.create!(:non_poly_two_id => NonPolyTwo.sample.id) end 1.upto(NUM_SHAPE_EXPRESSIONS) do - shape_type = [Circle, Square, Triangle].random_element - paint_type = [PaintColor, PaintTexture].random_element - ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.random_element.id, - :paint_type => paint_type.to_s, :paint_id => paint_type.random_element.id) + shape_type = [Circle, Square, Triangle].sample + paint_type = [PaintColor, PaintTexture].sample + ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.sample.id, + :paint_type => paint_type.to_s, :paint_id => paint_type.sample.id) end end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 91d8d2828b..33ffb041c1 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -301,7 +301,7 @@ class NamedScopeTest < ActiveRecord::TestCase end def test_rand_should_select_a_random_object_from_proxy - assert_kind_of Topic, Topic.approved.random_element + assert_kind_of Topic, Topic.approved.sample end def test_should_use_where_in_query_for_named_scope diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index d853788e00..0e24cc138a 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -4,7 +4,7 @@ * Ruby 1.9: support UTF-8 case folding. #4595 [Norman Clarke] -* Renames Array#rand -> Array#random_element. [Santiago Pastorino, Rizwan Reza] +* Removes Array#rand and backports Array#sample from Ruby 1.9, thanks to Marc-Andre Lafortune. [fxn] * Ruby 1.9: Renames last_(month|year) to prev_(month|year) in Date and Time. [fxn] diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb index 67c322daea..7a4836cecd 100644 --- a/activesupport/lib/active_support/core_ext/array/random_access.rb +++ b/activesupport/lib/active_support/core_ext/array/random_access.rb @@ -1,6 +1,20 @@ class Array - # Returns a random element from the array. - def random_element - self[Kernel.rand(length)] - end + # Backport of Array#sample based on Marc-Andre Lafortune's http://github.com/marcandre/backports/ + def sample(n=nil) + return self[Kernel.rand(size)] if n.nil? + n = n.to_int + rescue Exception => e + raise TypeError, "Coercion error: #{n.inspect}.to_int => Integer failed:\n(#{e.message})" + else + raise TypeError, "Coercion error: obj.to_int did NOT return an Integer (was #{n.class})" unless n.kind_of? Integer + raise ArgumentError, "negative array size" if n < 0 + n = size if n > size + result = Array.new(self) + n.times do |i| + r = i + Kernel.rand(size - i) + result[i], result[r] = result[r], result[i] + end + result[n..size] = [] + result + end unless method_defined? :sample end
\ No newline at end of file diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 1f7cdb8ec1..54376deee5 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -359,14 +359,30 @@ class ArrayUniqByTests < Test::Unit::TestCase end class ArrayExtRandomTests < ActiveSupport::TestCase - def test_random_element_from_array - assert_nil [].random_element - - Kernel.expects(:rand).with(1).returns(0) - assert_equal 'x', ['x'].random_element + def test_sample_from_array + assert_nil [].sample + assert_equal [], [].sample(5) + assert_equal 42, [42].sample + assert_equal [42], [42].sample(5) + + a = [:foo, :bar, 42] + s = a.sample(2) + assert_equal 2, s.size + assert_equal 1, (a-s).size + assert_equal [], a-(0..20).sum{a.sample(2)} + + o = Object.new + def o.to_int; 1; end + assert_equal [0], [0].sample(o) + + o = Object.new + assert_raises(TypeError) { [0].sample(o) } + + o = Object.new + def o.to_int; ''; end + assert_raises(TypeError) { [0].sample(o) } - Kernel.expects(:rand).with(3).returns(1) - assert_equal 2, [1, 2, 3].random_element + assert_raises(ArgumentError) { [0].sample(-7) } end end diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile index 2a8d7fbcdb..75c03be87c 100644 --- a/railties/guides/source/3_0_release_notes.textile +++ b/railties/guides/source/3_0_release_notes.textile @@ -512,6 +512,7 @@ These are the main changes in Active Support: * Active Support no longer provides vendored versions of "TZInfo":http://tzinfo.rubyforge.org/, "Memcache Client":http://deveiate.org/projects/RMemCache/ and "Builder":http://builder.rubyforge.org/, these are all included as dependencies and installed via the <tt>bundle install</tt> command. * Safe buffers are implemented in <tt>ActiveSupport::SafeBuffer</tt>. * Added <tt>Array.uniq_by</tt> and <tt>Array.uniq_by!</tt>. +* Removed <tt>Array#rand</tt> and backported <tt>Array#sample</tt> from Ruby 1.9. * Fixed bug on +TimeZone.seconds_to_utc_offset+ returning wrong value. * Added <tt>ActiveSupport::Notifications</tt> middleware. * <tt>ActiveSupport.use_standard_json_time_format</tt> now defaults to true. diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index de82e871a6..6c3a005daf 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -1927,13 +1927,23 @@ Similarly, +from+ returns the tail from the element at the passed index on: The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding element (+first+ is builtin). Thanks to social wisdom and positive constructiveness all around, +forty_two+ is also available. -You can pick a random element with +random_element+: +NOTE: Defined in +active_support/core_ext/array/access.rb+. + +h4. Random Access + +Active Support backports +sample+ from Ruby 1.9: + +You can pick a random element with +sample+: <ruby> -shape_type = [Circle, Square, Triangle].random_element +shape_type = [Circle, Square, Triangle].sample +# => Square, for example + +shape_types = [Circle, Square, Triangle].sample(2) +# => [Triangle, Circle], for example </ruby> -NOTE: Defined in +active_support/core_ext/array/access.rb+. +NOTE: Defined in +active_support/core_ext/array/random_access.rb+. h4. Options Extraction diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index fadcc4c025..35ea2729d3 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -47,7 +47,7 @@ module ApplicationTests test "if there's no config.active_support.bare, all of ActiveSupport is required" do use_frameworks [] require "#{app_path}/config/environment" - assert_nothing_raised { [1,2,3].random_element } + assert_nothing_raised { [1,2,3].sample } end test "config.active_support.bare does not require all of ActiveSupport" do @@ -57,7 +57,7 @@ module ApplicationTests Dir.chdir("#{app_path}/app") do require "#{app_path}/config/environment" - assert_raises(NoMethodError) { [1,2,3].random_element } + assert_raises(NoMethodError) { [1,2,3].sample } end end |