diff options
29 files changed, 243 insertions, 120 deletions
diff --git a/.travis.yml b/.travis.yml index 9a0ace81bb..6c98d8ea33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,4 +17,6 @@ notifications: on_failure: always channels: - "irc.freenode.org#rails-contrib" + campfire: + secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ=" bundler_args: --path vendor/bundle diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 1731388bf3..7470897659 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -298,11 +298,11 @@ module ActionController # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave" # assert flash.empty? # makes sure that there's nothing in the flash # - # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To + # For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing. - # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work. + # So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work. # - # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url. + # On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>. # # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another # action call which can then be asserted against. diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 8a9f9c4315..25affb9f50 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -103,7 +103,7 @@ module Mime SET << Mime.const_get(symbol.to_s.upcase) ([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup - ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last } + ([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last } end def parse(accept_header) diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb index ccfa858cce..f06c07daa5 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb @@ -1,10 +1,15 @@ <h1>Routing Error</h1> <p><pre><%=h @exception.message %></pre></p> -<% unless @exception.failures.empty? %><p> - <h2>Failure reasons:</h2> - <ol> - <% @exception.failures.each do |route, reason| %> - <li><code><%=h route.inspect.gsub('\\', '') %></code> failed because <%=h reason.downcase %></li> - <% end %> - </ol> -</p><% end %> +<% unless @exception.failures.empty? %> + <p> + <h2>Failure reasons:</h2> + <ol> + <% @exception.failures.each do |route, reason| %> + <li><code><%=h route.inspect.gsub('\\', '') %></code> failed because <%=h reason.downcase %></li> + <% end %> + </ol> + </p> +<% end %> +<p> + Try running <code>rake routes</code> for more information on available routes. +</p>
\ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 7d01e5ddb8..653e12c7d8 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -228,23 +228,19 @@ module ActionView ) end - # Web browsers cache favicons. If you just throw a <tt>favicon.ico</tt> into the document - # root of your application and it changes later, clients that have it in their cache - # won't see the update. Using this helper prevents that because it appends an asset ID: - # # <%= favicon_link_tag %> # # generates # - # <link href="/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" /> + # <link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> # # You may specify a different file in the first argument: # - # <%= favicon_link_tag 'favicon.ico' %> + # <%= favicon_link_tag '/myicon.ico' %> # # That's passed to +path_to_image+ as is, so it gives # - # <link href="/images/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" /> + # <link href="/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> # # The helper accepts an additional options hash where you can override "rel" and "type". # diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index b414327d08..5252e43c25 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -72,6 +72,7 @@ class ActionPackAssertionsController < ActionController::Base end def render_with_layout + @variable_for_layout = nil render "test/hello_world", :layout => "layouts/standard" end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 08fe2127b9..db21080c42 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -95,6 +95,37 @@ class MimeTypeTest < ActiveSupport::TestCase end end + test "custom type with type aliases" do + begin + Mime::Type.register "text/foobar", :foobar, ["text/foo", "text/bar"] + %w[text/foobar text/foo text/bar].each do |type| + assert_equal Mime::FOOBAR, type + end + ensure + Mime::Type.unregister(:FOOBAR) + end + end + + test "custom type with extension aliases" do + begin + Mime::Type.register "text/foobar", :foobar, [], [:foo, "bar"] + %w[foobar foo bar].each do |extension| + assert_equal Mime::FOOBAR, Mime::EXTENSION_LOOKUP[extension] + end + ensure + Mime::Type.unregister(:FOOBAR) + end + end + + test "register alias" do + begin + Mime::Type.register_alias "application/xhtml+xml", :foobar + assert_equal Mime::HTML, Mime::EXTENSION_LOOKUP['foobar'] + ensure + Mime::Type.unregister(:FOOBAR) + end + end + test "type should be equal to symbol" do assert_equal Mime::HTML, 'application/xhtml+xml' assert_equal Mime::HTML, :html diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 8337b04c0d..aafd1c8a74 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -176,8 +176,9 @@ module ActiveModel end # Returns true if no errors are found, false otherwise. + # If the error message is a string it can be empty. def empty? - all? { |k, v| v && v.empty? } + all? { |k, v| v && v.empty? && !v.is_a?(String) } end alias_method :blank?, :empty? diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 779f6c8448..575154ffbd 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -16,6 +16,12 @@ class ValidatesTest < ActiveModel::TestCase PersonWithValidator.reset_callbacks(:validate) end + def test_validates_with_messages_empty + Person.validates :title, :presence => {:message => "" } + person = Person.new + assert !person.valid?, 'person should not be valid.' + end + def test_validates_with_built_in_validation Person.validates :title, :numericality => true person = Person.new diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 207080973c..fe9f30bd2a 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -49,10 +49,18 @@ module ActiveRecord end else column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}" + relation = scoped - scoped.select(column).map! do |record| - record.send(reflection.association_primary_key) + including = (relation.eager_load_values + relation.includes_values).uniq + + if including.any? + join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, []) + relation = join_dependency.join_associations.inject(relation) do |r, association| + association.join_relation(r) + end end + + relation.uniq.pluck(column) end end diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index f575b26d04..5d37088d98 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -72,7 +72,7 @@ module ActiveRecord when :table_name_with_underscore base_name.foreign_key else - if ActiveRecord::Base != self && table_name + if ActiveRecord::Base != self && table_exists? connection.schema_cache.primary_keys[table_name] else 'id' diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b479c403a7..432a40ea54 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -699,7 +699,7 @@ module ActiveRecord #:nodoc: include Associations include IdentityMap include ActiveModel::SecurePassword - include Explain + extend Explain # AutosaveAssociation needs to be included before Transactions, because we want # #save_with_autosave_associations to be wrapped inside a transaction. diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index c9e85391cd..b64390250d 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -1,85 +1,83 @@ -require 'active_support/concern' +require 'active_support/core_ext/class/attribute' module ActiveRecord module Explain - extend ActiveSupport::Concern - - included do - # If a query takes longer than these many seconds we log its query plan - # automatically. nil disables this feature. - class_attribute :auto_explain_threshold_in_seconds, :instance_writer => false - self.auto_explain_threshold_in_seconds = nil + def self.extended(base) + base.class_eval do + # If a query takes longer than these many seconds we log its query plan + # automatically. nil disables this feature. + class_attribute :auto_explain_threshold_in_seconds, :instance_writer => false + self.auto_explain_threshold_in_seconds = nil + end end - module ClassMethods - # If auto explain is enabled, this method triggers EXPLAIN logging for the - # queries triggered by the block if it takes more than the threshold as a - # whole. That is, the threshold is not checked against each individual - # query, but against the duration of the entire block. This approach is - # convenient for relations. - # - # The available_queries_for_explain thread variable collects the queries - # to be explained. If the value is nil, it means queries are not being - # currently collected. A false value indicates collecting is turned - # off. Otherwise it is an array of queries. - def logging_query_plan # :nodoc: - threshold = auto_explain_threshold_in_seconds - current = Thread.current - if threshold && current[:available_queries_for_explain].nil? - begin - queries = current[:available_queries_for_explain] = [] - start = Time.now - result = yield - logger.warn(exec_explain(queries)) if Time.now - start > threshold - result - ensure - current[:available_queries_for_explain] = nil - end - else - yield + # If auto explain is enabled, this method triggers EXPLAIN logging for the + # queries triggered by the block if it takes more than the threshold as a + # whole. That is, the threshold is not checked against each individual + # query, but against the duration of the entire block. This approach is + # convenient for relations. + # + # The available_queries_for_explain thread variable collects the queries + # to be explained. If the value is nil, it means queries are not being + # currently collected. A false value indicates collecting is turned + # off. Otherwise it is an array of queries. + def logging_query_plan # :nodoc: + threshold = auto_explain_threshold_in_seconds + current = Thread.current + if threshold && current[:available_queries_for_explain].nil? + begin + queries = current[:available_queries_for_explain] = [] + start = Time.now + result = yield + logger.warn(exec_explain(queries)) if Time.now - start > threshold + result + ensure + current[:available_queries_for_explain] = nil end + else + yield end + end - # Relation#explain needs to be able to collect the queries regardless of - # whether auto explain is enabled. This method serves that purpose. - def collecting_queries_for_explain # :nodoc: - current = Thread.current - original, current[:available_queries_for_explain] = current[:available_queries_for_explain], [] - return yield, current[:available_queries_for_explain] - ensure - # Note that the return value above does not depend on this assigment. - current[:available_queries_for_explain] = original - end + # Relation#explain needs to be able to collect the queries regardless of + # whether auto explain is enabled. This method serves that purpose. + def collecting_queries_for_explain # :nodoc: + current = Thread.current + original, current[:available_queries_for_explain] = current[:available_queries_for_explain], [] + return yield, current[:available_queries_for_explain] + ensure + # Note that the return value above does not depend on this assigment. + current[:available_queries_for_explain] = original + end - # Makes the adapter execute EXPLAIN for the tuples of queries and bindings. - # Returns a formatted string ready to be logged. - def exec_explain(queries) # :nodoc: - queries && queries.map do |sql, bind| - [].tap do |msg| - msg << "EXPLAIN for: #{sql}" - unless bind.empty? - bind_msg = bind.map {|col, val| [col.name, val]}.inspect - msg.last << " #{bind_msg}" - end - msg << connection.explain(sql, bind) - end.join("\n") + # Makes the adapter execute EXPLAIN for the tuples of queries and bindings. + # Returns a formatted string ready to be logged. + def exec_explain(queries) # :nodoc: + queries && queries.map do |sql, bind| + [].tap do |msg| + msg << "EXPLAIN for: #{sql}" + unless bind.empty? + bind_msg = bind.map {|col, val| [col.name, val]}.inspect + msg.last << " #{bind_msg}" + end + msg << connection.explain(sql, bind) end.join("\n") - end + end.join("\n") + end - # Silences automatic EXPLAIN logging for the duration of the block. - # - # This has high priority, no EXPLAINs will be run even if downwards - # the threshold is set to 0. - # - # As the name of the method suggests this only applies to automatic - # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run. - def silence_auto_explain - current = Thread.current - original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false - yield - ensure - current[:available_queries_for_explain] = original - end + # Silences automatic EXPLAIN logging for the duration of the block. + # + # This has high priority, no EXPLAINs will be run even if downwards + # the threshold is set to 0. + # + # As the name of the method suggests this only applies to automatic + # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run. + def silence_auto_explain + current = Thread.current + original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false + yield + ensure + current[:available_queries_for_explain] = original end end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index f8a2de0f7e..794a0526fb 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -461,7 +461,7 @@ module ActiveRecord source_reflection.source_macro end - # A through association is nested iff there would be more than one join table + # A through association is nested if there would be more than one join table def nested? chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index ab2882516e..258c1959a0 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -9,7 +9,7 @@ module ActiveRecord MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind] SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order, :uniq] - include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain::ClassMethods, Delegation + include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation attr_reader :table, :klass, :loaded attr_accessor :extensions, :default_scoped diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index f7512bbf5f..17122740da 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -177,7 +177,7 @@ module ActiveRecord extension = Module.new(&Proc.new) if block_given? scope_proc = lambda do |*args| - options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options + options = scope_options.respond_to?(:call) ? unscoped { scope_options.call(*args) } : scope_options options = scoped.apply_finder_options(options) if options.is_a?(Hash) relation = scoped.merge(options) diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index bc6ebb076e..510411ecb2 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -825,7 +825,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase # clear cache possibly created by other tests david.projects.reset_column_information - # One query for columns, one for primary key, one for table existence assert_queries(1) { david.projects.columns; david.projects.columns } ## and again to verify that reset_column_information clears the cache correctly diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 7a6aba6a6b..c7bc275cf6 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -528,6 +528,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort end + def test_get_ids_for_has_many_through_with_conditions_should_not_preload + Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc)) + ActiveRecord::Associations::Preloader.expects(:new).never + posts(:welcome).misc_tag_ids + end + def test_get_ids_for_loaded_associations person = people(:michael) person.posts(true) diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 4a09a87322..0eb3d900bd 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -337,6 +337,11 @@ class NamedScopeTest < ActiveRecord::TestCase end end + def test_should_not_duplicates_where_values + where_values = Topic.where("1=1").scope_with_lambda.where_values + assert_equal ["1=1"], where_values + end + def test_chaining_with_duplicate_joins join = "INNER JOIN comments ON comments.post_id = posts.id" post = Post.find(1) diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index fe424e61b2..ede662450e 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -8,6 +8,8 @@ class Topic < ActiveRecord::Base scope :approved, :conditions => {:approved => true} scope :rejected, :conditions => {:approved => false} + scope :scope_with_lambda, lambda { scoped } + scope :by_lifo, :conditions => {:author_name => 'lifo'} scope :approved_as_hash_condition, :conditions => {:topics => {:approved => true}} diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 9711ed6f73..7d032ca984 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -91,7 +91,7 @@ module ActiveSupport def retrieve_cache_key(key) case when key.respond_to?(:cache_key) then key.cache_key - when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param + when key.is_a?(Array) then ['Array', *key.map { |element| retrieve_cache_key(element) }].to_param else key.to_param end.to_s end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index ab29ddecc5..5488070d8c 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -4,19 +4,19 @@ require 'active_support/cache' class CacheKeyTest < ActiveSupport::TestCase def test_expand_cache_key - assert_equal '1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true]) - assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name) + assert_equal 'Array/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true]) + assert_equal 'name/Array/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name) end def test_expand_cache_key_with_rails_cache_id begin ENV['RAILS_CACHE_ID'] = 'c99' assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo) - assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo]) - assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar]) + assert_equal 'c99/Array/foo', ActiveSupport::Cache.expand_cache_key([:foo]) + assert_equal 'c99/Array/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar]) assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm) - assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) - assert_equal 'nm/c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm) + assert_equal 'nm/c99/Array/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) + assert_equal 'nm/c99/Array/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm) ensure ENV['RAILS_CACHE_ID'] = nil end @@ -55,7 +55,7 @@ class CacheKeyTest < ActiveSupport::TestCase def key.cache_key :foo_key end - assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key]) + assert_equal 'Array/foo_key', ActiveSupport::Cache.expand_cache_key([key]) end def test_expand_cache_key_of_nil @@ -69,6 +69,14 @@ class CacheKeyTest < ActiveSupport::TestCase def test_expand_cache_key_of_true assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true) end + + def test_expand_cache_key_of_one_element_array_different_than_key_of_element + element = 'foo' + array = [element] + element_cache_key = ActiveSupport::Cache.expand_cache_key(element) + array_cache_key = ActiveSupport::Cache.expand_cache_key(array) + assert_not_equal element_cache_key, array_cache_key + end end class CacheStoreSettingTest < ActiveSupport::TestCase diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 0bd39cb2cd..197b692469 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -194,16 +194,32 @@ module Rails def assets_gemfile_entry return if options[:skip_sprockets] - <<-GEMFILE.strip_heredoc.gsub(/^[ \t]*$/, '') - # Gems used only for assets and not required - # in production environments by default. - group :assets do - gem 'sass-rails', :git => 'git://github.com/rails/sass-rails.git' - gem 'coffee-rails', :git => 'git://github.com/rails/coffee-rails.git' - #{"gem 'therubyrhino'\n" if defined?(JRUBY_VERSION)} - gem 'uglifier', '>= 1.0.3' - end - GEMFILE + + gemfile = if options.dev? || options.edge? + <<-GEMFILE + # Gems used only for assets and not required + # in production environments by default. + group :assets do + gem 'sass-rails', :git => 'git://github.com/rails/sass-rails.git' + gem 'coffee-rails', :git => 'git://github.com/rails/coffee-rails.git' + #{"gem 'therubyrhino'\n" if defined?(JRUBY_VERSION)} + gem 'uglifier', '>= 1.0.3' + end + GEMFILE + else + <<-GEMFILE + # Gems used only for assets and not required + # in production environments by default. + group :assets do + gem 'sass-rails', '~> 3.2.0' + gem 'coffee-rails', '~> 3.2.0' + #{"gem 'therubyrhino'\n" if defined?(JRUBY_VERSION)} + gem 'uglifier', '>= 1.0.3' + end + GEMFILE + end + + gemfile.strip_heredoc.gsub(/^[ \t]*$/, '') end def javascript_gemfile_entry diff --git a/railties/lib/rails/generators/rails/task/USAGE b/railties/lib/rails/generators/rails/task/USAGE new file mode 100644 index 0000000000..dbe9bbaf08 --- /dev/null +++ b/railties/lib/rails/generators/rails/task/USAGE @@ -0,0 +1,9 @@ +Description: + Stubs out a new Rake task. Pass the namespace name, and a list of tasks as arguments. + + This generates a task file in lib/tasks. + +Example: + `rails generate task feeds fetch erase add` + + Task: lib/tasks/feeds.rake
\ No newline at end of file diff --git a/railties/lib/rails/generators/rails/task/task_generator.rb b/railties/lib/rails/generators/rails/task/task_generator.rb new file mode 100644 index 0000000000..8a62d9e8eb --- /dev/null +++ b/railties/lib/rails/generators/rails/task/task_generator.rb @@ -0,0 +1,12 @@ +module Rails + module Generators + class TaskGenerator < NamedBase + argument :actions, :type => :array, :default => [], :banner => "action action" + + def create_task_files + template 'task.rb', File.join('lib/tasks', "#{file_name}.rake") + end + + end + end +end diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb new file mode 100644 index 0000000000..b7407bd6dc --- /dev/null +++ b/railties/lib/rails/generators/rails/task/templates/task.rb @@ -0,0 +1,8 @@ +namespace :<%= file_name %> do +<% actions.each do |action| -%> + desc "TODO" + task :<%= action %> => :environment do + end + +<% end -%> +end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 30fbe74e83..c456347e5c 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -1,4 +1,3 @@ -require 'abstract_unit' require 'generators/generators_test_helper' require 'rails/generators/rails/app/app_generator' require 'generators/shared_generator_tests.rb' @@ -129,7 +128,7 @@ class AppGeneratorTest < Rails::Generators::TestCase absolute = File.expand_path("Gemfile", destination_root) File.open(absolute, 'r') do |f| f.each_line do |line| - assert_no_match /^[ \t]+$/, line + assert_no_match %r{/^[ \t]+$/}, line end end end diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index 826eac904e..b62bd4b131 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -1,4 +1,3 @@ -require 'abstract_unit' require 'generators/generators_test_helper' require 'rails/generators/rails/plugin_new/plugin_new_generator' require 'generators/shared_generator_tests.rb' diff --git a/railties/test/generators/task_generator_test.rb b/railties/test/generators/task_generator_test.rb new file mode 100644 index 0000000000..f810a21d1e --- /dev/null +++ b/railties/test/generators/task_generator_test.rb @@ -0,0 +1,12 @@ +require 'generators/generators_test_helper' +require 'rails/generators/rails/task/task_generator' + +class TaskGeneratorTest < Rails::Generators::TestCase + include GeneratorsTestHelper + arguments %w(feeds foo bar) + + def test_controller_skeleton_is_created + run_generator + assert_file "lib/tasks/feeds.rake", /namespace :feeds/ + end +end |