aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md49
-rw-r--r--actionpack/lib/action_controller/test_case.rb55
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb4
-rw-r--r--actionpack/lib/action_view/test_case.rb6
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb22
-rw-r--r--actionpack/test/dispatch/routing_test.rb20
-rw-r--r--actionpack/test/dispatch/session/cache_store_test.rb1
-rw-r--r--actionpack/test/fixtures/test/hello/hello.erb1
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb8
-rw-r--r--activesupport/CHANGELOG.md2
-rw-r--r--activesupport/lib/active_support/cache.rb148
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb1
-rw-r--r--activesupport/test/caching_test.rb93
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb45
-rw-r--r--railties/test/generators/namespaced_generators_test.rb76
-rw-r--r--railties/test/generators/scaffold_generator_test.rb2
16 files changed, 387 insertions, 146 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 248677688f..c8f1102776 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,49 @@
## Rails 4.0.0 (unreleased) ##
+* Rename internal variables on ActionController::TemplateAssertions to prevent
+ naming collisions. @partials, @templates and @layouts are now prefixed with an underscore.
+ Fix #7459
+
+ *Yves Senn*
+
+* `resource` and `resources` don't modify the passed options hash
+ Fix #7777
+
+ *Yves Senn*
+
+* Precompiled assets include aliases from foo.js to foo/index.js and vice versa.
+
+ # Precompiles phone-<digest>.css and aliases phone/index.css to phone.css.
+ config.assets.precompile = [ 'phone.css' ]
+
+ # Precompiles phone/index-<digest>.css and aliases phone.css to phone/index.css.
+ config.assets.precompile = [ 'phone/index.css' ]
+
+ # Both of these work with either precompile thanks to their aliases.
+ <%= stylesheet_link_tag 'phone', media: 'all' %>
+ <%= stylesheet_link_tag 'phone/index', media: 'all' %>
+
+ *Jeremy Kemper*
+
+* `assert_template` is no more passing with what ever string that matches
+ with the template name.
+
+ Before when we have a template `/layout/hello.html.erb`, `assert_template`
+ was passing with any string that matches. This behavior allowed false
+ positive like:
+
+ assert_template "layout"
+ assert_template "out/hello"
+
+ Now it only passes with:
+
+ assert_template "layout/hello"
+ assert_template "hello"
+
+ Fixes #3849.
+
+ *Hugolnx*
+
* `image_tag` will set the same width and height for image if numerical value
passed to `size` option.
@@ -44,9 +88,8 @@
*Luiz Felipe Garcia Pereira*
-* Sprockets integration has been extracted from Action Pack and the `sprockets-rails`
- gem should be added to Gemfile (under the assets group) in order to use Rails asset
- pipeline in future versions of Rails.
+* Sprockets integration has been extracted from Action Pack to the `sprockets-rails`
+ gem. `rails` gem is depending on `sprockets-rails` by default.
*Guillermo Iguaran*
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 0caeef3192..3af378173a 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -12,16 +12,16 @@ module ActionController
end
def setup_subscriptions
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
path = payload[:layout]
if path
- @layouts[path] += 1
+ @_layouts[path] += 1
if path =~ /^layouts\/(.*)/
- @layouts[$1] += 1
+ @_layouts[$1] += 1
end
end
end
@@ -32,11 +32,11 @@ module ActionController
partial = path =~ /^.*\/_[^\/]*$/
if partial
- @partials[path] += 1
- @partials[path.split("/").last] += 1
+ @_partials[path] += 1
+ @_partials[path.split("/").last] += 1
end
- @templates[path] += 1
+ @_templates[path] += 1
end
end
@@ -46,9 +46,9 @@ module ActionController
end
def process(*args)
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
super
end
@@ -86,31 +86,38 @@ module ActionController
response.body
case options
- when NilClass, String, Symbol, Regexp
+ when NilClass, Regexp, String, Symbol
options = options.to_s if Symbol === options
- rendered = @templates
+ rendered = @_templates
msg = message || sprintf("expecting <%s> but rendering with <%s>",
options.inspect, rendered.keys)
matches_template =
- if options
+ case options
+ when String
+ rendered.any? do |t, num|
+ options_splited = options.split(File::SEPARATOR)
+ t_splited = t.split(File::SEPARATOR)
+ t_splited.last(options_splited.size) == options_splited
+ end
+ when Regexp
rendered.any? { |t,num| t.match(options) }
- else
- @templates.blank?
+ when NilClass
+ rendered.blank?
end
assert matches_template, msg
when Hash
if options.key?(:layout)
expected_layout = options[:layout]
msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
- expected_layout, @layouts.keys)
+ expected_layout, @_layouts.keys)
case expected_layout
when String, Symbol
- assert_includes @layouts.keys, expected_layout.to_s, msg
+ assert_includes @_layouts.keys, expected_layout.to_s, msg
when Regexp
- assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
+ assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
when nil, false
- assert(@layouts.empty?, msg)
+ assert(@_layouts.empty?, msg)
end
end
@@ -121,17 +128,17 @@ module ActionController
assert_equal(v, actual_locals[k])
end
elsif expected_count = options[:count]
- actual_count = @partials[expected_partial]
+ actual_count = @_partials[expected_partial]
msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
expected_partial, expected_count, actual_count)
assert(actual_count == expected_count.to_i, msg)
else
msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
- options[:partial], @partials.keys)
- assert_includes @partials, expected_partial, msg
+ options[:partial], @_partials.keys)
+ assert_includes @_partials, expected_partial, msg
end
elsif options.key?(:partial)
- assert @partials.empty?,
+ assert @_partials.empty?,
"Expected no partials to be rendered"
end
else
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 49afa01d25..c5cf413c8f 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1038,7 +1038,7 @@ module ActionDispatch
# === Options
# Takes same options as +resources+.
def resource(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resource, resources, options, &block)
return self
@@ -1204,7 +1204,7 @@ module ActionDispatch
# # resource actions are at /admin/posts.
# resources :posts, :path => "admin/posts"
def resources(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resources, resources, options, &block)
return self
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index b7fd6ec7e1..5434b3421e 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -196,17 +196,17 @@ module ActionView
:@_result,
:@_routes,
:@controller,
- :@layouts,
+ :@_layouts,
:@locals,
:@method_name,
:@output_buffer,
- :@partials,
+ :@_partials,
:@passed,
:@rendered,
:@request,
:@routes,
:@tagged_logger,
- :@templates,
+ :@_templates,
:@options,
:@test_passed,
:@view,
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 4ab5d92a2b..ca542eb7e2 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -7,6 +7,7 @@ class ActionPackAssertionsController < ActionController::Base
def nothing() head :ok end
def hello_world() render :template => "test/hello_world"; end
+ def hello_repeating_in_path() render :template => "test/hello/hello"; end
def hello_xml_world() render :template => "test/hello_xml_world"; end
@@ -464,6 +465,20 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_with_incorrect_string_that_matches
+ get :hello_world
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template 'est/he'
+ end
+ end
+
+ def test_fails_with_repeated_name_in_path
+ get :hello_repeating_in_path
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template 'test/hello'
+ end
+ end
+
def test_fails_with_incorrect_symbol
get :hello_world
assert_raise(ActiveSupport::TestCase::Assertion) do
@@ -471,6 +486,13 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_with_incorrect_symbol_that_matches
+ get :hello_world
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template :"est/he"
+ end
+ end
+
def test_fails_with_wrong_layout
get :render_with_layout
assert_raise(ActiveSupport::TestCase::Assertion) do
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 4e83ad16d7..93d89f7568 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1124,6 +1124,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/sheep/1/_it', _it_sheep_path(1)
end
+ def test_resource_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resource :user, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
+ def test_resources_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resources :users, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
def test_path_names
get '/pt/projetos'
assert_equal 'projects#index', @response.body
diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb
index a74e165826..b8479e8836 100644
--- a/actionpack/test/dispatch/session/cache_store_test.rb
+++ b/actionpack/test/dispatch/session/cache_store_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'fixtures/session_autoload_test/session_autoload_test/foo'
class CacheStoreTest < ActionDispatch::IntegrationTest
class TestController < ActionController::Base
diff --git a/actionpack/test/fixtures/test/hello/hello.erb b/actionpack/test/fixtures/test/hello/hello.erb
new file mode 100644
index 0000000000..6769dd60bd
--- /dev/null
+++ b/actionpack/test/fixtures/test/hello/hello.erb
@@ -0,0 +1 @@
+Hello world! \ No newline at end of file
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index f3620c1324..5dece1cb36 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -64,14 +64,12 @@ module ActiveRecord
end
def build_relation(klass, table, attribute, value) #:nodoc:
- reflection = klass.reflect_on_association(attribute)
- if reflection
- column = klass.columns_hash[reflection.foreign_key]
+ if reflection = klass.reflect_on_association(attribute)
attribute = reflection.foreign_key
value = value.attributes[reflection.primary_key_column.name]
- else
- column = klass.columns_hash[attribute.to_s]
end
+
+ column = klass.columns_hash[attribute.to_s]
value = column.limit ? value.to_s[0, column.limit] : value.to_s if !value.nil? && column.text?
if !options[:case_sensitive] && value && column.text?
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 2df3d1f69b..ce1053393e 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. *Brian Durand*
+
* Tests tag the Rails log with the current test class and test case:
[SessionsControllerTest] [test_0002_sign in] Processing by SessionsController#create as HTML
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index f98ba16cdd..690e5ce194 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -284,7 +284,9 @@ module ActiveSupport
end
if entry && entry.expired?
race_ttl = options[:race_condition_ttl].to_i
- if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
+ if race_ttl && (Time.now - entry.expires_at <= race_ttl)
+ # When an entry has :race_condition_ttl defined, put the stale entry back into the cache
+ # for a brief period while the entry is begin recalculated.
entry.expires_at = Time.now + race_ttl
write_entry(key, entry, :expires_in => race_ttl * 2)
else
@@ -532,102 +534,122 @@ module ActiveSupport
end
end
- # Entry that is put into caches. It supports expiration time on entries and
- # can compress values to save space in the cache.
- class Entry
- attr_reader :created_at, :expires_in
-
+ # This class is used to represent cache entries. Cache entries have a value and an optional
+ # expiration time. The expiration time is used to support the :race_condition_ttl option
+ # on the cache.
+ #
+ # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
+ # using short instance variable names that are lazily defined.
+ class Entry # :nodoc:
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
- class << self
- # Create an entry with internal attributes set. This method is intended
- # to be used by implementations that store cache entries in a native
- # format instead of as serialized Ruby objects.
- def create(raw_value, created_at, options = {})
- entry = new(nil)
- entry.instance_variable_set(:@value, raw_value)
- entry.instance_variable_set(:@created_at, created_at.to_f)
- entry.instance_variable_set(:@compressed, options[:compressed])
- entry.instance_variable_set(:@expires_in, options[:expires_in])
- entry
- end
- end
-
# Create a new cache entry for the specified value. Options supported are
# +:compress+, +:compress_threshold+, and +:expires_in+.
def initialize(value, options = {})
- @compressed = false
- @expires_in = options[:expires_in]
- @expires_in = @expires_in.to_f if @expires_in
- @created_at = Time.now.to_f
- if value.nil?
- @value = nil
+ if should_compress?(value, options)
+ @v = compress(value)
+ @c = true
else
- @value = Marshal.dump(value)
- if should_compress?(@value, options)
- @value = Zlib::Deflate.deflate(@value)
- @compressed = true
- end
+ @v = value
end
- end
-
- # Get the raw value. This value may be serialized and compressed.
- def raw_value
- @value
- end
-
- # Get the value stored in the cache.
- def value
- # If the original value was exactly false @value is still true because
- # it is marshalled and eventually compressed. Both operations yield
- # strings.
- if @value
- Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
+ if expires_in = options[:expires_in]
+ @x = (Time.now + expires_in).to_i
end
end
- def compressed?
- @compressed
+ def value
+ convert_version_3_entry! if defined?(@value)
+ compressed? ? uncompress(@v) : @v
end
# Check if the entry is expired. The +expires_in+ parameter can override
# the value set when the entry was created.
def expired?
- @expires_in && @created_at + @expires_in <= Time.now.to_f
- end
-
- # Set a new time when the entry will expire.
- def expires_at=(time)
- if time
- @expires_in = time.to_f - @created_at
+ convert_version_3_entry! if defined?(@value)
+ if defined?(@x)
+ @x && @x < Time.now.to_i
else
- @expires_in = nil
+ false
end
end
- # Seconds since the epoch when the entry will expire.
def expires_at
- @expires_in ? @created_at + @expires_in : nil
+ Time.at(@x) if defined?(@x)
+ end
+
+ def expires_at=(value)
+ @x = value.to_i
end
# Returns the size of the cached value. This could be less than
# <tt>value.size</tt> if the data is compressed.
def size
- if @value.nil?
- 0
+ if defined?(@s)
+ @s
else
- @value.bytesize
+ case value
+ when NilClass
+ 0
+ when String
+ @v.bytesize
+ else
+ @s = Marshal.dump(@v).bytesize
+ end
+ end
+ end
+
+ # Duplicate the value in a class. This is used by cache implementations that don't natively
+ # serialize entries to protect against accidental cache modifications.
+ def dup_value!
+ convert_version_3_entry! if defined?(@value)
+ if @v && !compressed? && !(@v.is_a?(Numeric) || @v == true || @v == false)
+ if @v.is_a?(String)
+ @v = @v.dup
+ else
+ @v = Marshal.load(Marshal.dump(@v))
+ end
end
end
private
- def should_compress?(serialized_value, options)
- if options[:compress]
+ def should_compress?(value, options)
+ if value && options[:compress]
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
- return true if serialized_value.size >= compress_threshold
+ serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
+ return true if serialized_value_size >= compress_threshold
end
false
end
+
+ def compressed?
+ defined?(@c) ? @c : false
+ end
+
+ def compress(value)
+ Zlib::Deflate.deflate(Marshal.dump(value))
+ end
+
+ def uncompress(value)
+ Marshal.load(Zlib::Inflate.inflate(value))
+ end
+
+ # The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
+ # to ensure that cache entries created under the old version still work with the new class definition.
+ def convert_version_3_entry!
+ if defined?(@value)
+ @v = @value
+ remove_instance_variable(:@value)
+ end
+ if defined?(@compressed)
+ @c = @compressed
+ remove_instance_variable(:@compressed)
+ end
+ if defined?(@expires_in) && defined?(@created_at)
+ @x = (@created_at + @expires_in).to_i
+ remove_instance_variable(:@created_at)
+ remove_instance_variable(:@expires_in)
+ end
+ 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 7fd5e3b53d..4d26fb7e42 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -135,6 +135,7 @@ module ActiveSupport
end
def write_entry(key, entry, options) # :nodoc:
+ entry.dup_value!
synchronize do
old_entry = @data[key]
return false if @data.key?(key) && options[:unless_exist]
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 71cd9d81b3..febf0eeeff 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -224,25 +224,22 @@ module CacheStoreBehavior
end
def test_read_multi_with_expires
- @cache.write('foo', 'bar', :expires_in => 0.001)
+ time = Time.now
+ @cache.write('foo', 'bar', :expires_in => 10)
@cache.write('fu', 'baz')
@cache.write('fud', 'biz')
- sleep(0.002)
+ Time.stubs(:now).returns(time + 11)
assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
end
def test_read_and_write_compressed_small_data
@cache.write('foo', 'bar', :compress => true)
- raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
assert_equal 'bar', @cache.read('foo')
- assert_equal 'bar', Marshal.load(raw_value)
end
def test_read_and_write_compressed_large_data
@cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
- raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
assert_equal 'bar', @cache.read('foo')
- assert_equal 'bar', Marshal.load(Zlib::Inflate.inflate(raw_value))
end
def test_read_and_write_compressed_nil
@@ -301,14 +298,6 @@ module CacheStoreBehavior
assert !@cache.exist?('foo')
end
- def test_read_should_return_a_different_object_id_each_time_it_is_called
- @cache.write('foo', 'bar')
- assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
- value = @cache.read('foo')
- value << 'bingo'
- assert_not_equal value, @cache.read('foo')
- end
-
def test_original_store_objects_should_not_be_immutable
bar = 'bar'
@cache.write('foo', bar)
@@ -363,7 +352,7 @@ module CacheStoreBehavior
rescue ArgumentError
end
assert_equal "bar", @cache.read('foo')
- Time.stubs(:now).returns(time + 71)
+ Time.stubs(:now).returns(time + 91)
assert_nil @cache.read('foo')
end
@@ -627,7 +616,7 @@ end
class MemoryStoreTest < ActiveSupport::TestCase
def setup
- @record_size = Marshal.dump("aaaaaaaaaa").bytesize
+ @record_size = ActiveSupport::Cache::Entry.new("aaaaaaaaaa").size
@cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10)
end
@@ -646,9 +635,9 @@ class MemoryStoreTest < ActiveSupport::TestCase
@cache.prune(@record_size * 3)
assert @cache.exist?(5)
assert @cache.exist?(4)
- assert !@cache.exist?(3)
+ assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1)
+ assert !@cache.exist?(1), "no entry"
end
def test_prune_size_on_write
@@ -670,12 +659,12 @@ class MemoryStoreTest < ActiveSupport::TestCase
assert @cache.exist?(9)
assert @cache.exist?(8)
assert @cache.exist?(7)
- assert !@cache.exist?(6)
- assert !@cache.exist?(5)
+ assert !@cache.exist?(6), "no entry"
+ assert !@cache.exist?(5), "no entry"
assert @cache.exist?(4)
- assert !@cache.exist?(3)
+ assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1)
+ assert !@cache.exist?(1), "no entry"
end
def test_pruning_is_capped_at_a_max_time
@@ -764,6 +753,14 @@ class MemCacheStoreTest < ActiveSupport::TestCase
assert_equal [], cache.read("foo")
end
end
+
+ def test_read_should_return_a_different_object_id_each_time_it_is_called
+ @cache.write('foo', 'bar')
+ assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
+ value = @cache.read('foo')
+ value << 'bingo'
+ assert_not_equal value, @cache.read('foo')
+ end
end
class NullStoreTest < ActiveSupport::TestCase
@@ -844,15 +841,6 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase
end
class CacheEntryTest < ActiveSupport::TestCase
- def test_create_raw_entry
- time = Time.now
- entry = ActiveSupport::Cache::Entry.create("raw", time, :compress => false, :expires_in => 300)
- assert_equal "raw", entry.raw_value
- assert_equal time.to_f, entry.created_at
- assert !entry.compressed?
- assert_equal 300, entry.expires_in
- end
-
def test_expired
entry = ActiveSupport::Cache::Entry.new("value")
assert !entry.expired?, 'entry not expired'
@@ -864,16 +852,43 @@ class CacheEntryTest < ActiveSupport::TestCase
end
def test_compress_values
- entry = ActiveSupport::Cache::Entry.new("value", :compress => true, :compress_threshold => 1)
- assert_equal "value", entry.value
- assert entry.compressed?
- assert_equal "value", Marshal.load(Zlib::Inflate.inflate(entry.raw_value))
+ value = "value" * 100
+ entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
+ assert_equal value, entry.value
+ assert(value.bytesize > entry.size, "value is compressed")
end
def test_non_compress_values
- entry = ActiveSupport::Cache::Entry.new("value")
- assert_equal "value", entry.value
- assert_equal "value", Marshal.load(entry.raw_value)
- assert !entry.compressed?
+ value = "value" * 100
+ entry = ActiveSupport::Cache::Entry.new(value)
+ assert_equal value, entry.value
+ assert_equal value.bytesize, entry.size
+ end
+
+ def test_restoring_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, "hello")
+ version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ assert_equal false, entry.expired?
+ end
+
+ def test_restoring_compressed_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, Zlib::Deflate.deflate(Marshal.dump("hello")))
+ version_3_entry.instance_variable_set(:@compressed, true)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ end
+
+ def test_restoring_expired_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, "hello")
+ version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
+ version_3_entry.instance_variable_set(:@expires_in, 58.9)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ assert_equal true, entry.expired?
end
end
diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
index 6a5d62803c..f33d56b564 100644
--- a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -1,13 +1,50 @@
module Rails
module Generators
class ResourceRouteGenerator < NamedBase
+
+ # Properly nests namespaces passed into a generator
+ #
+ # $ rails generate resource admin/users/products
+ #
+ # should give you
+ #
+ # namespace :admin do
+ # namespace :users
+ # resources :products
+ # end
+ # end
def add_resource_route
return if options[:actions].present?
- route_config = regular_class_path.collect{ |namespace| "namespace :#{namespace} do " }.join(" ")
- route_config << "resources :#{file_name.pluralize}"
- route_config << " end" * regular_class_path.size
- route route_config
+
+ # iterates over all namespaces and opens up blocks
+ regular_class_path.each_with_index do |namespace, index|
+ write("namespace :#{namespace} do", index + 1)
+ end
+
+ # inserts the primary resource
+ write("resources :#{file_name.pluralize}", route_length + 1)
+
+ # ends blocks
+ regular_class_path.each_index do |index|
+ write("end", route_length - index)
+ end
+
+ # route prepends two spaces onto the front of the string that is passed, this corrects that
+ route route_string[2..-1]
end
+
+ private
+ def route_string
+ @route_string ||= ""
+ end
+
+ def write(str, indent)
+ route_string << "#{" " * indent}#{str}\n"
+ end
+
+ def route_length
+ regular_class_path.length
+ end
end
end
end
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index db2b8af217..ede779ea59 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -304,7 +304,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller
@@ -346,7 +346,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Route
assert_file "config/routes.rb" do |route|
- assert_no_match(/namespace :admin do resources :roles end$/, route)
+ assert_no_match(/^ namespace :admin do\n resources :roles\n end$$/, route)
end
# Controller
@@ -364,4 +364,76 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Stylesheets (should not be removed)
assert_file "app/assets/stylesheets/scaffold.css"
end
+
+ def test_scaffold_with_nested_namespace_on_invoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/user/special/role.rb", /module TestApp\n class Admin::User::Special::Role < ActiveRecord::Base/
+ assert_file "test/unit/test_app/admin/user/special/role_test.rb", /module TestApp\n class Admin::User::Special::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/user/special/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::User::Special::RolesController < ApplicationController/, content)
+ end
+
+ assert_file "test/functional/test_app/admin/user/special/roles_controller_test.rb",
+ /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(
+ index
+ edit
+ new
+ show
+ _form
+ ).each { |view| assert_file "app/views/test_app/admin/user/special/roles/#{view}.html.erb" }
+ assert_no_file "app/views/layouts/admin/user/special/roles.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_file "test/unit/helpers/test_app/admin/user/special/roles_helper_test.rb"
+
+ # Stylesheets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_revoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+ run_generator [ "admin/user/special/role" ], :behavior => :revoke
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb" # ( should not be remove )
+ assert_no_file "app/models/test_app/admin/user/special/role.rb"
+ assert_no_file "test/unit/test_app/admin/user/special/role_test.rb"
+ assert_no_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_no_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/test_app/admin/user/special/roles_controller.rb"
+ assert_no_file "test/functional/test_app/admin/user/special/roles_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/test_app/admin/user/special/roles"
+
+ # Helpers
+ assert_no_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_no_file "test/unit/helpers/test_app/admin/user/special/roles_helper_test.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 40c5188042..f802f3c4ad 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -142,7 +142,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller