From 73056500f88d569fa497d846dfe6b501a9e03739 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 6 Aug 2008 14:39:13 -0500 Subject: Make File.atomic_write copy the original permissions or use the directories default. --- activesupport/lib/active_support/core_ext/file.rb | 24 ++--------- .../lib/active_support/core_ext/file/atomic.rb | 46 ++++++++++++++++++++ activesupport/test/core_ext/file_test.rb | 50 +++++++++++++++++++--- 3 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/file/atomic.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb index 45d93b220f..e03f8ac44e 100644 --- a/activesupport/lib/active_support/core_ext/file.rb +++ b/activesupport/lib/active_support/core_ext/file.rb @@ -1,21 +1,5 @@ -require 'tempfile' +require 'active_support/core_ext/file/atomic' -# Write to a file atomically. Useful for situations where you don't -# want other processes or threads to see half-written files. -# -# File.atomic_write("important.file") do |file| -# file.write("hello") -# end -# -# If your temp directory is not on the same filesystem as the file you're -# trying to write, you can provide a different temporary directory. -# -# File.atomic_write("/data/something.important", "/data/tmp") do |f| -# file.write("hello") -# end -def File.atomic_write(file_name, temp_dir = Dir.tmpdir) - temp_file = Tempfile.new(File.basename(file_name), temp_dir) - yield temp_file - temp_file.close - File.rename(temp_file.path, file_name) -end \ No newline at end of file +class File #:nodoc: + extend ActiveSupport::CoreExtensions::File::Atomic +end diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb new file mode 100644 index 0000000000..4d3cf5423f --- /dev/null +++ b/activesupport/lib/active_support/core_ext/file/atomic.rb @@ -0,0 +1,46 @@ +require 'tempfile' + +module ActiveSupport #:nodoc: + module CoreExtensions #:nodoc: + module File #:nodoc: + module Atomic + # Write to a file atomically. Useful for situations where you don't + # want other processes or threads to see half-written files. + # + # File.atomic_write("important.file") do |file| + # file.write("hello") + # end + # + # If your temp directory is not on the same filesystem as the file you're + # trying to write, you can provide a different temporary directory. + # + # File.atomic_write("/data/something.important", "/data/tmp") do |f| + # file.write("hello") + # end + def atomic_write(file_name, temp_dir = Dir.tmpdir) + temp_file = Tempfile.new(basename(file_name), temp_dir) + yield temp_file + temp_file.close + + begin + # Get original file permissions + old_stat = stat(file_name) + rescue Errno::ENOENT + # No old permissions, write a temp file to determine the defaults + check_name = ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}" + new(check_name, "w") + old_stat = stat(check_name) + unlink(check_name) + end + + # Overwrite original file with temp file + rename(temp_file.path, file_name) + + # Set correct permissions on new file + chown(old_stat.uid, old_stat.gid, file_name) + chmod(old_stat.mode, file_name) + end + end + end + end +end diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb index 5efe357e9f..63b470684f 100644 --- a/activesupport/test/core_ext/file_test.rb +++ b/activesupport/test/core_ext/file_test.rb @@ -1,9 +1,8 @@ require 'abstract_unit' class AtomicWriteTest < Test::Unit::TestCase - def test_atomic_write_without_errors - contents = "Atomic Text" + contents = "Atomic Text" File.atomic_write(file_name, Dir.pwd) do |file| file.write(contents) assert !File.exist?(file_name) @@ -13,7 +12,7 @@ class AtomicWriteTest < Test::Unit::TestCase ensure File.unlink(file_name) rescue nil end - + def test_atomic_write_doesnt_write_when_block_raises File.atomic_write(file_name) do |file| file.write("testing") @@ -22,8 +21,47 @@ class AtomicWriteTest < Test::Unit::TestCase rescue assert !File.exist?(file_name) end - - def file_name - "atomic.file" + + def test_atomic_write_preserves_file_permissions + contents = "Atomic Text" + File.open(file_name, "w", 0755) do |file| + file.write(contents) + assert File.exist?(file_name) + end + assert File.exist?(file_name) + assert_equal "100755", file_mode + assert_equal contents, File.read(file_name) + + File.atomic_write(file_name, Dir.pwd) do |file| + file.write(contents) + assert File.exist?(file_name) + end + assert File.exist?(file_name) + assert_equal "100755", file_mode + assert_equal contents, File.read(file_name) + ensure + File.unlink(file_name) rescue nil + end + + def test_atomic_write_preserves_default_file_permissions + contents = "Atomic Text" + File.atomic_write(file_name, Dir.pwd) do |file| + file.write(contents) + assert !File.exist?(file_name) + end + assert File.exist?(file_name) + assert_equal "100644", file_mode + assert_equal contents, File.read(file_name) + ensure + File.unlink(file_name) rescue nil end + + private + def file_name + "atomic.file" + end + + def file_mode + sprintf("%o", File.stat(file_name).mode) + end end -- cgit v1.2.3 From e5b1ab7cc39ff57f9789ffda75fb33f72187775d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 6 Aug 2008 14:50:02 -0500 Subject: MemoryStore is the only "unsafe" store. Make it threadsafe by default. --- activesupport/lib/active_support/cache.rb | 18 ------ .../lib/active_support/cache/memory_store.rb | 55 ++++++++++++++---- activesupport/test/caching_test.rb | 67 ---------------------- 3 files changed, 43 insertions(+), 97 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 5a064f8bea..95eae3a77e 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -39,10 +39,6 @@ module ActiveSupport class Store cattr_accessor :logger - def threadsafe! - extend ThreadSafety - end - def silence! @silence = true self @@ -115,20 +111,6 @@ module ActiveSupport logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off end end - - module ThreadSafety #:nodoc: - def self.extended(object) #:nodoc: - object.instance_variable_set(:@mutex, Mutex.new) - end - - %w(read write delete delete_matched exist? increment decrement).each do |method| - module_eval <<-EOS, __FILE__, __LINE__ - def #{method}(*args) - @mutex.synchronize { super } - end - EOS - end - end end end diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb index 6f114273e4..9332d50f24 100644 --- a/activesupport/lib/active_support/cache/memory_store.rb +++ b/activesupport/lib/active_support/cache/memory_store.rb @@ -3,36 +3,67 @@ module ActiveSupport class MemoryStore < Store def initialize @data = {} + @mutex = Mutex.new + end + + def fetch(key, options = {}) + @mutex.synchronize do + super + end end def read(name, options = nil) - super - @data[name] + @mutex.synchronize do + super + @data[name] + end end def write(name, value, options = nil) - super - @data[name] = value + @mutex.synchronize do + super + @data[name] = value + end end def delete(name, options = nil) - super - @data.delete(name) + @mutex.synchronize do + super + @data.delete(name) + end end def delete_matched(matcher, options = nil) - super - @data.delete_if { |k,v| k =~ matcher } + @mutex.synchronize do + super + @data.delete_if { |k,v| k =~ matcher } + end end def exist?(name,options = nil) - super - @data.has_key?(name) + @mutex.synchronize do + super + @data.has_key?(name) + end + end + + def increment(key, amount = 1) + @mutex.synchronize do + super + end + end + + def decrement(key, amount = 1) + @mutex.synchronize do + super + end end def clear - @data.clear + @mutex.synchronize do + @data.clear + end end end end -end \ No newline at end of file +end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 0af4251962..f3220d27aa 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -70,70 +70,3 @@ uses_mocha 'high-level cache store tests' do end end end - -class ThreadSafetyCacheStoreTest < Test::Unit::TestCase - def setup - @cache = ActiveSupport::Cache.lookup_store(:memory_store).threadsafe! - @cache.write('foo', 'bar') - - # No way to have mocha proxy to the original method - @mutex = @cache.instance_variable_get(:@mutex) - @mutex.instance_eval %( - def calls; @calls; end - def synchronize - @calls ||= 0 - @calls += 1 - yield - end - ) - end - - def test_read_is_synchronized - assert_equal 'bar', @cache.read('foo') - assert_equal 1, @mutex.calls - end - - def test_write_is_synchronized - @cache.write('foo', 'baz') - assert_equal 'baz', @cache.read('foo') - assert_equal 2, @mutex.calls - end - - def test_delete_is_synchronized - assert_equal 'bar', @cache.read('foo') - @cache.delete('foo') - assert_equal nil, @cache.read('foo') - assert_equal 3, @mutex.calls - end - - def test_delete_matched_is_synchronized - assert_equal 'bar', @cache.read('foo') - @cache.delete_matched(/foo/) - assert_equal nil, @cache.read('foo') - assert_equal 3, @mutex.calls - end - - def test_fetch_is_synchronized - assert_equal 'bar', @cache.fetch('foo') { 'baz' } - assert_equal 'fu', @cache.fetch('bar') { 'fu' } - assert_equal 3, @mutex.calls - end - - def test_exist_is_synchronized - assert @cache.exist?('foo') - assert !@cache.exist?('bar') - assert_equal 2, @mutex.calls - end - - def test_increment_is_synchronized - @cache.write('foo_count', 1) - assert_equal 2, @cache.increment('foo_count') - assert_equal 4, @mutex.calls - end - - def test_decrement_is_synchronized - @cache.write('foo_count', 1) - assert_equal 0, @cache.decrement('foo_count') - assert_equal 4, @mutex.calls - end -end -- cgit v1.2.3 From dfc83566b3f52b4b84db52312c01fcc3b8847059 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 6 Aug 2008 14:52:39 -0500 Subject: Make FileStore use atomic writes --- activesupport/lib/active_support/cache/file_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 5b771b1da0..0bdca49388 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -15,7 +15,7 @@ module ActiveSupport def write(name, value, options = nil) super ensure_cache_path(File.dirname(real_file_path(name))) - File.open(real_file_path(name), "wb+") { |f| f.write(value) } + File.atomic_write(real_file_path(name)) { |f| f.write(value) } rescue => e RAILS_DEFAULT_LOGGER.error "Couldn't create cache directory: #{name} (#{e.message})" if RAILS_DEFAULT_LOGGER end -- cgit v1.2.3 From fbc6129acd9ecb6b7435931b472d3226985ba4c4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 6 Aug 2008 17:03:42 -0500 Subject: Treat single C operations in MemoryStore as atomic --- .../lib/active_support/cache/memory_store.rb | 31 ++++++---------------- 1 file changed, 8 insertions(+), 23 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb index 9332d50f24..a44f877414 100644 --- a/activesupport/lib/active_support/cache/memory_store.rb +++ b/activesupport/lib/active_support/cache/memory_store.rb @@ -13,38 +13,25 @@ module ActiveSupport end def read(name, options = nil) - @mutex.synchronize do - super - @data[name] - end + super + @data[name] end def write(name, value, options = nil) - @mutex.synchronize do - super - @data[name] = value - end + super + @data[name] = value end def delete(name, options = nil) - @mutex.synchronize do - super - @data.delete(name) - end + @data.delete(name) end def delete_matched(matcher, options = nil) - @mutex.synchronize do - super - @data.delete_if { |k,v| k =~ matcher } - end + @data.delete_if { |k,v| k =~ matcher } end def exist?(name,options = nil) - @mutex.synchronize do - super - @data.has_key?(name) - end + @data.has_key?(name) end def increment(key, amount = 1) @@ -60,9 +47,7 @@ module ActiveSupport end def clear - @mutex.synchronize do - @data.clear - end + @data.clear end end end -- cgit v1.2.3 From c6b7d0f34472ee7ef03d602c8923fd0ba8dab833 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 6 Aug 2008 17:22:58 -0500 Subject: Ensure file atomic write uses the cache directory as its tmp folder --- activesupport/lib/active_support/cache/file_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 0bdca49388..7b6ca39091 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -15,7 +15,7 @@ module ActiveSupport def write(name, value, options = nil) super ensure_cache_path(File.dirname(real_file_path(name))) - File.atomic_write(real_file_path(name)) { |f| f.write(value) } + File.atomic_write(real_file_path(name), cache_path) { |f| f.write(value) } rescue => e RAILS_DEFAULT_LOGGER.error "Couldn't create cache directory: #{name} (#{e.message})" if RAILS_DEFAULT_LOGGER end -- cgit v1.2.3 From ed8a882e47e07b470b71cacd8cd50e251dca4d27 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 6 Aug 2008 17:31:57 -0700 Subject: JRuby: improve constantize performance. [#410 state:resolved] --- activesupport/lib/active_support/inflector.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 6651569d33..c2738b39fc 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -291,11 +291,14 @@ module ActiveSupport # NameError is raised when the name is not in CamelCase or the constant is # unknown. def constantize(camel_cased_word) - unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word - raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" - end + names = camel_cased_word.split('::') + names.shift if names.empty? || names.first.empty? - Object.module_eval("::#{$1}", __FILE__, __LINE__) + constant = Object + names.each do |name| + constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name) + end + constant end # Turns a number into an ordinal string used to denote the position in an @@ -326,4 +329,4 @@ require 'active_support/inflections' require 'active_support/core_ext/string/inflections' unless String.included_modules.include?(ActiveSupport::CoreExtensions::String::Inflections) String.send :include, ActiveSupport::CoreExtensions::String::Inflections -end \ No newline at end of file +end -- cgit v1.2.3 From be0d235a3b82e72de49592913753a1f291a98f86 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 6 Aug 2008 20:21:15 -0500 Subject: Optimize memoized method if there are no arguments --- activesupport/lib/active_support/memoizable.rb | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 23dd96e4df..7e2e28712c 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -29,14 +29,24 @@ module ActiveSupport raise "Already memoized #{symbol}" if method_defined?(:#{original_method}) alias #{original_method} #{symbol} - def #{symbol}(*args) - #{memoized_ivar} ||= {} - reload = args.pop if args.last == true || args.last == :reload + if instance_method(:#{symbol}).arity == 0 + def #{symbol}(reload = false) + if !reload && defined? #{memoized_ivar} + #{memoized_ivar} + else + #{memoized_ivar} = #{original_method}.freeze + end + end + else + def #{symbol}(*args) + #{memoized_ivar} ||= {} + reload = args.pop if args.last == true || args.last == :reload - if !reload && #{memoized_ivar} && #{memoized_ivar}.has_key?(args) - #{memoized_ivar}[args] - else - #{memoized_ivar}[args] = #{original_method}(*args).freeze + if !reload && #{memoized_ivar} && #{memoized_ivar}.has_key?(args) + #{memoized_ivar}[args] + else + #{memoized_ivar}[args] = #{original_method}(*args).freeze + end end end EOS -- cgit v1.2.3 From a8057669ff6ba11e228fc73eef7390b826d0de25 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 7 Aug 2008 14:55:14 -0500 Subject: Fixed memoize with punctuation and freezing memoized methods with arguments Signed-off-by: Joshua Peek --- activesupport/lib/active_support/memoizable.rb | 15 +++++++++++---- activesupport/test/memoizable_test.rb | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 7e2e28712c..e6049d9496 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -10,9 +10,16 @@ module ActiveSupport end def freeze_with_memoizable - methods.each do |method| - __send__($1) if method.to_s =~ /^_unmemoized_(.*)/ - end unless frozen? + unless frozen? + methods.each do |method| + if method.to_s =~ /^_unmemoized_(.*)/ + begin + __send__($1) + rescue ArgumentError + end + end + end + end freeze_without_memoizable end @@ -21,7 +28,7 @@ module ActiveSupport def memoize(*symbols) symbols.each do |symbol| original_method = "_unmemoized_#{symbol}" - memoized_ivar = "@_memoized_#{symbol}" + memoized_ivar = "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}" class_eval <<-EOS, __FILE__, __LINE__ include Freezable diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index cd84dcda53..01631dc5e1 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -16,6 +16,16 @@ uses_mocha 'Memoizable' do "Josh" end + def name? + true + end + memoize :name? + + def update(name) + "Joshua" + end + memoize :update + def age @age_calls += 1 nil @@ -88,6 +98,10 @@ uses_mocha 'Memoizable' do assert_equal 1, @person.name_calls end + def test_memoization_with_punctuation + assert_equal true, @person.name? + end + def test_memoization_with_nil_value assert_equal nil, @person.age assert_equal 1, @person.age_calls @@ -114,6 +128,7 @@ uses_mocha 'Memoizable' do def test_memoized_is_not_affected_by_freeze @person.freeze assert_equal "Josh", @person.name + assert_equal "Joshua", @person.update("Joshua") end def test_memoization_with_args -- cgit v1.2.3 From 6c70c02c83b3a03c88df4286130be8882f6d201c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 7 Aug 2008 23:31:43 -0700 Subject: Freeze memoized results when instance is frozen instead of immediately so you can memoize mutable objects --- activesupport/lib/active_support/memoizable.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index e6049d9496..b5adc2330c 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -14,7 +14,7 @@ module ActiveSupport methods.each do |method| if method.to_s =~ /^_unmemoized_(.*)/ begin - __send__($1) + __send__($1).freeze rescue ArgumentError end end @@ -41,7 +41,7 @@ module ActiveSupport if !reload && defined? #{memoized_ivar} #{memoized_ivar} else - #{memoized_ivar} = #{original_method}.freeze + #{memoized_ivar} = #{original_method} end end else @@ -52,7 +52,7 @@ module ActiveSupport if !reload && #{memoized_ivar} && #{memoized_ivar}.has_key?(args) #{memoized_ivar}[args] else - #{memoized_ivar}[args] = #{original_method}(*args).freeze + #{memoized_ivar}[args] = #{original_method}(*args) end end end -- cgit v1.2.3 From 282b42021306f73d4cc8f8a06713da9a55ed044b Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Wed, 13 Aug 2008 00:51:47 -0400 Subject: Account for the possibility of a nil options argument to CompressedMemCacheStore#read/#write --- .../lib/active_support/cache/compressed_mem_cache_store.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb b/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb index 9470ac9f66..0bff6cf9ad 100644 --- a/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb @@ -1,14 +1,14 @@ module ActiveSupport module Cache class CompressedMemCacheStore < MemCacheStore - def read(name, options = {}) - if value = super(name, options.merge(:raw => true)) + def read(name, options = nil) + if value = super(name, (options || {}).merge(:raw => true)) Marshal.load(ActiveSupport::Gzip.decompress(value)) end end - def write(name, value, options = {}) - super(name, ActiveSupport::Gzip.compress(Marshal.dump(value)), options.merge(:raw => true)) + def write(name, value, options = nil) + super(name, ActiveSupport::Gzip.compress(Marshal.dump(value)), (options || {}).merge(:raw => true)) end end end -- cgit v1.2.3 From 3284fbb86629f398ba2634dd9369bc65beb7d6ae Mon Sep 17 00:00:00 2001 From: "S. Brent Faulkner" Date: Wed, 13 Aug 2008 19:19:00 -0500 Subject: Use current umask when testing the expected file mode [#823 state:resolved] Signed-off-by: Joshua Peek --- activesupport/test/core_ext/file_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activesupport') diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb index 63b470684f..eedc6b592b 100644 --- a/activesupport/test/core_ext/file_test.rb +++ b/activesupport/test/core_ext/file_test.rb @@ -29,7 +29,7 @@ class AtomicWriteTest < Test::Unit::TestCase assert File.exist?(file_name) end assert File.exist?(file_name) - assert_equal "100755", file_mode + assert_equal 0100755, file_mode assert_equal contents, File.read(file_name) File.atomic_write(file_name, Dir.pwd) do |file| @@ -37,7 +37,7 @@ class AtomicWriteTest < Test::Unit::TestCase assert File.exist?(file_name) end assert File.exist?(file_name) - assert_equal "100755", file_mode + assert_equal 0100755, file_mode assert_equal contents, File.read(file_name) ensure File.unlink(file_name) rescue nil @@ -50,7 +50,7 @@ class AtomicWriteTest < Test::Unit::TestCase assert !File.exist?(file_name) end assert File.exist?(file_name) - assert_equal "100644", file_mode + assert_equal 0100666 ^ File.umask, file_mode assert_equal contents, File.read(file_name) ensure File.unlink(file_name) rescue nil @@ -62,6 +62,6 @@ class AtomicWriteTest < Test::Unit::TestCase end def file_mode - sprintf("%o", File.stat(file_name).mode) + File.stat(file_name).mode end end -- cgit v1.2.3 From 3fc9a67c04bade858e7ac7eb8cd94eec6a63ec27 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 13 Aug 2008 17:22:38 -0700 Subject: memoize_ and unmemoize_all --- activesupport/lib/active_support/memoizable.rb | 51 +++++++++++++++++--------- activesupport/test/memoizable_test.rb | 15 ++++++++ 2 files changed, 48 insertions(+), 18 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index b5adc2330c..5aa73bce52 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -10,25 +10,37 @@ module ActiveSupport end def freeze_with_memoizable - unless frozen? - methods.each do |method| - if method.to_s =~ /^_unmemoized_(.*)/ - begin - __send__($1).freeze - rescue ArgumentError - end + memoize_all unless frozen? + freeze_without_memoizable + end + + def memoize_all + methods.each do |m| + if m.to_s =~ /^_unmemoized_(.*)/ + if method(m).arity == 0 + __send__($1) + else + ivar = :"@_memoized_#{$1}" + instance_variable_set(ivar, {}) end end end + end - freeze_without_memoizable + def unmemoize_all + methods.each do |m| + if m.to_s =~ /^_unmemoized_(.*)/ + ivar = :"@_memoized_#{$1}" + instance_variable_get(ivar).clear if instance_variable_defined?(ivar) + end + end end end def memoize(*symbols) symbols.each do |symbol| - original_method = "_unmemoized_#{symbol}" - memoized_ivar = "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}" + original_method = :"_unmemoized_#{symbol}" + memoized_ivar = :"@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}" class_eval <<-EOS, __FILE__, __LINE__ include Freezable @@ -38,21 +50,24 @@ module ActiveSupport if instance_method(:#{symbol}).arity == 0 def #{symbol}(reload = false) - if !reload && defined? #{memoized_ivar} - #{memoized_ivar} - else - #{memoized_ivar} = #{original_method} + if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? + #{memoized_ivar} = [#{original_method}] end + #{memoized_ivar}[0] end else def #{symbol}(*args) - #{memoized_ivar} ||= {} + #{memoized_ivar} ||= {} unless frozen? reload = args.pop if args.last == true || args.last == :reload - if !reload && #{memoized_ivar} && #{memoized_ivar}.has_key?(args) - #{memoized_ivar}[args] + if #{memoized_ivar} + if !reload && #{memoized_ivar}.has_key?(args) + #{memoized_ivar}[args] + elsif #{memoized_ivar} + #{memoized_ivar}[args] = #{original_method}(*args) + end else - #{memoized_ivar}[args] = #{original_method}(*args) + #{original_method}(*args) end end end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 01631dc5e1..99d9240ea1 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -119,6 +119,21 @@ uses_mocha 'Memoizable' do assert_equal 3, @calculator.counter end + def test_unmemoize_all + assert_equal 1, @calculator.counter + + assert @calculator.instance_variable_get(:@_memoized_counter).any? + @calculator.unmemoize_all + assert @calculator.instance_variable_get(:@_memoized_counter).empty? + + assert_equal 2, @calculator.counter + end + + def test_memoize_all + @calculator.memoize_all + assert @calculator.instance_variable_defined?(:@_memoized_counter) + end + def test_memoization_cache_is_different_for_each_instance assert_equal 1, @calculator.counter assert_equal 2, @calculator.counter(:reload) -- cgit v1.2.3 From b8b30985d525fd15b6c16d29fc115e83e3ee5037 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 13 Aug 2008 20:57:26 -0500 Subject: Marshal FileStore values --- .../lib/active_support/cache/file_store.rb | 4 ++-- activesupport/test/caching_test.rb | 27 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 7b6ca39091..437679cc05 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -9,13 +9,13 @@ module ActiveSupport def read(name, options = nil) super - File.open(real_file_path(name), 'rb') { |f| f.read } rescue nil + File.open(real_file_path(name), 'rb') { |f| Marshal.load(f) } rescue nil end def write(name, value, options = nil) super ensure_cache_path(File.dirname(real_file_path(name))) - File.atomic_write(real_file_path(name), cache_path) { |f| f.write(value) } + File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) } rescue => e RAILS_DEFAULT_LOGGER.error "Couldn't create cache directory: #{name} (#{e.message})" if RAILS_DEFAULT_LOGGER end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index f3220d27aa..c5f7fb7fdd 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -70,3 +70,30 @@ uses_mocha 'high-level cache store tests' do end end end + +class FileStoreTest < Test::Unit::TestCase + def setup + @cache = ActiveSupport::Cache.lookup_store(:file_store, Dir.pwd) + end + + def test_should_read_and_write_strings + @cache.write('foo', 'bar') + assert_equal 'bar', @cache.read('foo') + ensure + File.delete("foo.cache") + end + + def test_should_read_and_write_hash + @cache.write('foo', {:a => "b"}) + assert_equal({:a => "b"}, @cache.read('foo')) + ensure + File.delete("foo.cache") + end + + def test_should_read_and_write_nil + @cache.write('foo', nil) + assert_equal nil, @cache.read('foo') + ensure + File.delete("foo.cache") + end +end -- cgit v1.2.3 From 8cb14ee1203c9ed380c4b192e8730757a52d43cb Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 13 Aug 2008 21:30:46 -0500 Subject: Ensure results returned by a memoized method are immutable --- activesupport/lib/active_support/memoizable.rb | 4 ++-- activesupport/test/memoizable_test.rb | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 5aa73bce52..6506238ac0 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -51,7 +51,7 @@ module ActiveSupport if instance_method(:#{symbol}).arity == 0 def #{symbol}(reload = false) if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? - #{memoized_ivar} = [#{original_method}] + #{memoized_ivar} = [#{original_method}.freeze] end #{memoized_ivar}[0] end @@ -64,7 +64,7 @@ module ActiveSupport if !reload && #{memoized_ivar}.has_key?(args) #{memoized_ivar}[args] elsif #{memoized_ivar} - #{memoized_ivar}[args] = #{original_method}(*args) + #{memoized_ivar}[args] = #{original_method}(*args).freeze end else #{original_method}(*args) diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 99d9240ea1..135d56f14a 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -110,6 +110,11 @@ uses_mocha 'Memoizable' do assert_equal 1, @person.age_calls end + def test_memorized_results_are_immutable + assert_equal "Josh", @person.name + assert_raise(ActiveSupport::FrozenObjectError) { @person.name.gsub!("Josh", "Gosh") } + end + def test_reloadable counter = @calculator.counter assert_equal 1, @calculator.counter -- cgit v1.2.3