diff options
64 files changed, 1027 insertions, 768 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index acb9aff6aa..730dd2d7aa 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -420,12 +420,6 @@ module ActionMailer #:nodoc: new.deliver!(mail) end - def register_template_extension(extension) - ActiveSupport::Deprecation.warn( - "ActionMailer::Base.register_template_extension has been deprecated." + - "Use ActionView::Base.register_template_extension instead", caller) - end - def template_root self.view_paths && self.view_paths.first end diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb new file mode 100644 index 0000000000..1c92dd266d --- /dev/null +++ b/actionmailer/test/asset_host_test.rb @@ -0,0 +1,54 @@ +require 'abstract_unit' + +class AssetHostMailer < ActionMailer::Base + def email_with_asset(recipient) + recipients recipient + subject "testing email containing asset path while asset_host is set" + from "tester@example.com" + end +end + +class AssetHostTest < Test::Unit::TestCase + def setup + set_delivery_method :test + ActionMailer::Base.perform_deliveries = true + ActionMailer::Base.deliveries = [] + + @recipient = 'test@localhost' + end + + def teardown + restore_delivery_method + end + + def test_asset_host_as_string + ActionController::Base.asset_host = "http://www.example.com" + mail = AssetHostMailer.deliver_email_with_asset(@recipient) + assert_equal "<img alt=\"Somelogo\" src=\"http://www.example.com/images/somelogo.png\" />", mail.body.strip + end + + def test_asset_host_as_one_arguement_proc + ActionController::Base.asset_host = Proc.new { |source| + if source.starts_with?('/images') + "http://images.example.com" + else + "http://assets.example.com" + end + } + mail = AssetHostMailer.deliver_email_with_asset(@recipient) + assert_equal "<img alt=\"Somelogo\" src=\"http://images.example.com/images/somelogo.png\" />", mail.body.strip + end + + def test_asset_host_as_two_arguement_proc + ActionController::Base.asset_host = Proc.new {|source,request| + if request && request.ssl? + "https://www.example.com" + else + "http://www.example.com" + end + } + mail = nil + assert_nothing_raised { mail = AssetHostMailer.deliver_email_with_asset(@recipient) } + assert_equal "<img alt=\"Somelogo\" src=\"http://www.example.com/images/somelogo.png\" />", mail.body.strip + end +end
\ No newline at end of file diff --git a/actionmailer/test/fixtures/asset_host_mailer/email_with_asset.html.erb b/actionmailer/test/fixtures/asset_host_mailer/email_with_asset.html.erb new file mode 100644 index 0000000000..b3f0438d03 --- /dev/null +++ b/actionmailer/test/fixtures/asset_host_mailer/email_with_asset.html.erb @@ -0,0 +1 @@ +<%= image_tag "somelogo.png" %>
\ No newline at end of file diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 1110c5cac6..352c4253f4 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *2.3.0 [Edge]* +* Allow users to opt out of the spoofing checks in Request#remote_ip. Useful for sites whose traffic regularly triggers false positives. [Darren Boyd] + * Deprecated formatted_polymorphic_url. [Jeremy Kemper] * Added the option to declare an asset_host as an object that responds to call (see http://github.com/dhh/asset-hosting-with-minimum-ssl for an example) [DHH] diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index da5f1e81e6..2981f625a1 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -57,6 +57,7 @@ module ActionController autoload :Integration, 'action_controller/integration' autoload :IntegrationTest, 'action_controller/integration' autoload :Layout, 'action_controller/layout' + autoload :MiddlewareStack, 'action_controller/middleware_stack' autoload :MimeResponds, 'action_controller/mime_responds' autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' autoload :RackRequest, 'action_controller/rack_process' diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index dca66ff0a5..c2f0c1c4f6 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -327,6 +327,10 @@ module ActionController #:nodoc: # sets it to <tt>:authenticity_token</tt> by default. cattr_accessor :request_forgery_protection_token + # Controls the IP Spoofing check when determining the remote IP. + @@ip_spoofing_check = true + cattr_accessor :ip_spoofing_check + # Indicates whether or not optimise the generated named # route helper methods cattr_accessor :optimise_named_routes diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index e7345621cc..47199af2b4 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -85,6 +85,9 @@ module ActionController end end + cattr_accessor :middleware + self.middleware = MiddlewareStack.new + cattr_accessor :error_file_path self.error_file_path = Rails.public_path if defined?(Rails.public_path) @@ -93,6 +96,7 @@ module ActionController def initialize(output = $stdout, request = nil, response = nil) @output, @request, @response = output, request, response + @app = @@middleware.build(lambda { |env| self._call(env) }) end def dispatch_unlocked @@ -127,6 +131,10 @@ module ActionController end def call(env) + @app.call(env) + end + + def _call(env) @request = RackRequest.new(env) @response = RackResponse.new(@request) dispatch diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_controller/middleware_stack.rb new file mode 100644 index 0000000000..1864bed23a --- /dev/null +++ b/actionpack/lib/action_controller/middleware_stack.rb @@ -0,0 +1,42 @@ +module ActionController + class MiddlewareStack < Array + class Middleware + attr_reader :klass, :args, :block + + def initialize(klass, *args, &block) + @klass = klass.is_a?(Class) ? klass : klass.to_s.constantize + @args = args + @block = block + end + + def ==(middleware) + case middleware + when Middleware + klass == middleware.klass + when Class + klass == middleware + else + klass == middleware.to_s.constantize + end + end + + def inspect + str = @klass.to_s + @args.each { |arg| str += ", #{arg.inspect}" } + str + end + + def build(app) + klass.new(app, *args, &block) + end + end + + def use(*args, &block) + push(Middleware.new(*args, &block)) + end + + def build(app) + reverse.inject(app) { |a, e| e.build(a) } + end + end +end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index baa955cb04..087fffe87d 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -218,7 +218,7 @@ module ActionController remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',') if @env.include? 'HTTP_CLIENT_IP' - if remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP']) + if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP']) # We don't know which came from the proxy, and which from the user raise ActionControllerError.new(<<EOM) IP spoofing attack?! diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 4ec7a383e5..0633d5414e 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -574,7 +574,7 @@ module ActionView private def request - @controller.request + request? && @controller.request end def request? diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 506138a735..1d9e4fe9b8 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -370,8 +370,8 @@ module ActionView options.reverse_merge!(:link => :all, :html => {}) case options[:link].to_sym - when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), &block) - when :email_addresses then auto_link_email_addresses(text, &block) + when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), options[:html], &block) + when :email_addresses then auto_link_email_addresses(text, options[:html], &block) when :urls then auto_link_urls(text, options[:html], &block) end end @@ -559,7 +559,7 @@ module ActionView # Turns all email addresses into clickable links. If a block is given, # each email is yielded and the result is used as the link text. - def auto_link_email_addresses(text) + def auto_link_email_addresses(text, html_options = {}) body = text.dup text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do text = $1 @@ -568,7 +568,7 @@ module ActionView text else display_text = (block_given?) ? yield(text) : text - %{<a href="mailto:#{text}">#{display_text}</a>} + mail_to text, display_text, html_options end end end diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb index 316a203e97..ba4a6da39b 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/controller/request_test.rb @@ -66,6 +66,15 @@ class RequestTest < ActiveSupport::TestCase assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message + # turn IP Spoofing detection off. + # This is useful for sites that are aimed at non-IP clients. The typical + # example is WAP. Since the cellular network is not IP based, it's a + # leap of faith to assume that their proxies are ever going to set the + # HTTP_CLIENT_IP/HTTP_X_FORWARDED_FOR headers properly. + ActionController::Base.ip_spoofing_check = false + assert_equal('8.8.8.8', @request.remote_ip(true)) + ActionController::Base.ip_spoofing_check = true + @request.env['HTTP_X_FORWARDED_FOR'] = '8.8.8.8, 9.9.9.9' assert_equal '8.8.8.8', @request.remote_ip(true) diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 3e7a8f3e44..a6200fbdd7 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -262,6 +262,11 @@ class TextHelperTest < ActionView::TestCase email2_result = %{<a href="mailto:#{email2_raw}">#{email2_raw}</a>} assert_equal email2_result, auto_link(email2_raw) + email3_raw = '+david@loudthinking.com' + email3_result = %{<a href="mailto:+%64%61%76%69%64@%6c%6f%75%64%74%68%69%6e%6b%69%6e%67.%63%6f%6d">#{email3_raw}</a>} + assert_equal email3_result, auto_link(email3_raw, :all, :encode => :hex) + assert_equal email3_result, auto_link(email3_raw, :email_addresses, :encode => :hex) + link2_raw = 'www.rubyonrails.com' link2_result = generate_result(link2_raw, "http://#{link2_raw}") assert_equal %(Go to #{link2_result}), auto_link("Go to #{link2_raw}", :urls) @@ -362,7 +367,7 @@ class TextHelperTest < ActionView::TestCase end def test_auto_link_with_options_hash - assert_dom_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me@email.com">me@email.com</a>.', + assert_dom_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me@email.com" class="menu" target="_blank">me@email.com</a>.', auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.", :link => :all, :html => { :class => "menu", :target => "_blank" }) end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index c1d7297260..cca70f1fb7 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *2.3.0/3.0* +* Add :having as a key to find and the relevant associations. [miloops] + * Added default_scope to Base #1381 [Paweł Kondzior]. Example: class Person < ActiveRecord::Base diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 69300e5ce5..99c3ce5e62 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -185,7 +185,7 @@ module ActiveRecord associated_records = reflection.klass.find(:all, :conditions => [conditions, ids], :include => options[:include], - :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} as t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", + :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id", :order => options[:order]) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 0546b76c63..3fbbea43ed 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -724,6 +724,8 @@ module ActiveRecord # Specify second-order associations that should be eager loaded when the collection is loaded. # [:group] # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause. + # [:having] + # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause. # [:limit] # An integer determining the limit on the number of rows that should be returned. # [:offset] @@ -1181,6 +1183,8 @@ module ActiveRecord # Specify second-order associations that should be eager loaded when the collection is loaded. # [:group] # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause. + # [:having] + # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause. # [:limit] # An integer determining the limit on the number of rows that should be returned. # [:offset] @@ -1553,7 +1557,7 @@ module ActiveRecord @@valid_keys_for_has_many_association = [ :class_name, :table_name, :foreign_key, :primary_key, :dependent, - :select, :conditions, :include, :order, :group, :limit, :offset, + :select, :conditions, :include, :order, :group, :having, :limit, :offset, :as, :through, :source, :source_type, :uniq, :finder_sql, :counter_sql, @@ -1609,7 +1613,7 @@ module ActiveRecord mattr_accessor :valid_keys_for_has_and_belongs_to_many_association @@valid_keys_for_has_and_belongs_to_many_association = [ :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key, - :select, :conditions, :include, :order, :group, :limit, :offset, + :select, :conditions, :include, :order, :group, :having, :limit, :offset, :uniq, :finder_sql, :counter_sql, :delete_sql, :insert_sql, :before_add, :after_add, :before_remove, :after_remove, @@ -1658,7 +1662,7 @@ module ActiveRecord add_conditions!(sql, options[:conditions], scope) add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - add_group!(sql, options[:group], scope) + add_group!(sql, options[:group], options[:having], scope) add_order!(sql, options[:order], scope) add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections) add_lock!(sql, options, scope) @@ -1714,7 +1718,7 @@ module ActiveRecord end add_conditions!(sql, options[:conditions], scope) - add_group!(sql, options[:group], scope) + add_group!(sql, options[:group], options[:having], scope) if order && is_distinct connection.add_order_by_for_association_limiting!(sql, :order => order) diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index d1a79df6e6..75ec4fbb2e 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -188,6 +188,7 @@ module ActiveRecord def merge_options_from_reflection!(options) options.reverse_merge!( :group => @reflection.options[:group], + :having => @reflection.options[:having], :limit => @reflection.options[:limit], :offset => @reflection.options[:offset], :joins => @reflection.options[:joins], diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9e4514375f..8f8ed241d5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -521,6 +521,7 @@ module ActiveRecord #:nodoc: # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>, or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro. # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name". # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause. + # * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause. # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned. # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4. # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed) @@ -1632,7 +1633,7 @@ module ActiveRecord #:nodoc: add_joins!(sql, options[:joins], scope) add_conditions!(sql, options[:conditions], scope) - add_group!(sql, options[:group], scope) + add_group!(sql, options[:group], options[:having], scope) add_order!(sql, options[:order], scope) add_limit!(sql, options, scope) add_lock!(sql, options, scope) @@ -1688,13 +1689,15 @@ module ActiveRecord #:nodoc: end end - def add_group!(sql, group, scope = :auto) + def add_group!(sql, group, having, scope = :auto) if group sql << " GROUP BY #{group}" + sql << " HAVING #{having}" if having else scope = scope(:find) if :auto == scope if scope && (scoped_group = scope[:group]) sql << " GROUP BY #{scoped_group}" + sql << " HAVING #{scoped_having}" if (scoped_having = scope[:having]) end end end @@ -2259,7 +2262,7 @@ module ActiveRecord #:nodoc: end VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, - :order, :select, :readonly, :group, :from, :lock ] + :order, :select, :readonly, :group, :having, :from, :lock ] def validate_find_options(options) #:nodoc: options.assert_valid_keys(VALID_FIND_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 b5bedf3704..2f08e09d43 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 @@ -658,6 +658,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 1, categories(:technology).posts_gruoped_by_title.size end + def test_find_scoped_grouped_having + assert_equal 2, projects(:active_record).well_payed_salary_groups.size + assert projects(:active_record).well_payed_salary_groups.all? { |g| g.salary > 10000 } + end + def test_get_ids assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort assert_equal [projects(:active_record).id], developers(:jamis).project_ids diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 59784e1bcb..816ceb6855 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -255,6 +255,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 2, companies(:first_firm).clients_grouped_by_name.length end + def test_find_scoped_grouped_having + assert_equal 1, authors(:david).popular_grouped_posts.length + assert_equal 0, authors(:mary).popular_grouped_posts.length + end + def test_adding force_signal37_to_load_all_clients_of_firm natural = Client.new("name" => "Natural Company") diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 153880afbd..d4d770b04e 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -175,6 +175,13 @@ class FinderTest < ActiveRecord::TestCase assert_equal 4, developers.map(&:salary).uniq.size end + def test_find_with_group_and_having + developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary") + assert_equal 3, developers.size + assert_equal 3, developers.map(&:salary).uniq.size + assert developers.all? { |developer| developer.salary > 10000 } + end + def test_find_with_entire_select_statement topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'" diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index e5b19ff9e4..4ffac4fe32 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -1,6 +1,7 @@ class Author < ActiveRecord::Base has_many :posts has_many :posts_with_comments, :include => :comments, :class_name => "Post" + has_many :popular_grouped_posts, :include => :comments, :class_name => "Post", :group => "type", :having => "SUM(comments_count) > 1", :select => "type" has_many :posts_with_comments_sorted_by_comment_id, :include => :comments, :class_name => "Post", :order => 'comments.id' has_many :posts_sorted_by_id_limited, :class_name => "Post", :order => 'posts.id', :limit => 1 has_many :posts_with_categories, :include => :categories, :class_name => "Post" diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb index 4e9d247a4e..5efce6aaa6 100644 --- a/activerecord/test/models/category.rb +++ b/activerecord/test/models/category.rb @@ -14,6 +14,7 @@ class Category < ActiveRecord::Base :class_name => 'Post', :conditions => { :title => 'Yet Another Testing Title' } + has_and_belongs_to_many :popular_grouped_posts, :class_name => "Post", :group => "posts.type", :having => "sum(comments.post_id) > 2", :include => :comments has_and_belongs_to_many :posts_gruoped_by_title, :class_name => "Post", :group => "title", :select => "title" def self.what_are_you diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index 44c692b5e7..550d4ae23c 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -13,6 +13,7 @@ class Project < ActiveRecord::Base :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"}, :before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"}, :after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"} + has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" attr_accessor :developers_log diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index bb284803d8..ad9ae6df1f 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -704,6 +704,7 @@ module ActiveResource def new? id.nil? end + alias :new_record? :new? # Gets the <tt>\id</tt> attribute of the resource. def id diff --git a/railties/CHANGELOG b/railties/CHANGELOG index ad8ba43c17..ca49c5d1c7 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,6 +1,51 @@ *2.3.0 [Edge]* -* Enhanced Rails.root to take parameters that'll be join with the root, like Rails.root('app', 'controllers') => File.join(Rails.root, 'app', 'controllers') #1482 [Damian Janowski] +* Add "-m/--template" option to Rails generator to apply a template to the generated application. [Jeremy McAnally] + + This has been extracted from rg - http://github.com/jeremymcanally/rg + + Example: + + # template.rb + + # Install plugins from git or svn + plugin "will-paginate", :git => "git://github.com/mislav/will_paginate.git" + plugin "old-restful-auth", :svn => "http://svn.techno-weenie.net/projects/plugins/restful_authentication/" + + # Add gems to environment.rb + gem "jeremymcanally-context" + gem "bluecloth" + + # Vendor file. Data in a string or... + vendor("borrowed.rb", <<CODE + def helpful_method + do_something_helpful_here + end + CODE + + # ...file data from block return value. + # #initializer creates a new initializer file + initializer("crypto.rb") do + salt = "--#{Time.now}--#{rand}--#{srand(Time.now.to_i)}" + + "SPECIAL_SALT = '#{salt}'" + end + + Usage: + + To use a template, provide a file path or URL: + + 1. Using a local file : + + rails <application name> -m /path/to/my/template.rb + + 2. Or directly from a URL : + + rails <application name> --template=http://gist.github.com/31208.txt + +* Extracted the process scripts (inspector, reaper, spawner) into the plugin irs_process_scripts [DHH] + +* Changed Rails.root to return a Pathname object (allows for Rails.root.join('app', 'controllers') => "#{RAILS_ROOT}/app/controllers") #1482 [Damian Janowski/?] * Added view path support for engines [DHH] diff --git a/railties/Rakefile b/railties/Rakefile index bf70219aa8..f812b42f1d 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -53,7 +53,6 @@ BASE_DIRS = %w( public script script/performance - script/process test vendor vendor/plugins @@ -71,7 +70,7 @@ LOG_FILES = %w( server.log development.log test.log production.log ) HTML_FILES = %w( 422.html 404.html 500.html index.html robots.txt favicon.ico images/rails.png javascripts/prototype.js javascripts/application.js javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js ) -BIN_FILES = %w( about console destroy generate performance/benchmarker performance/profiler process/reaper process/spawner process/inspector runner server plugin ) +BIN_FILES = %w( about console destroy generate performance/benchmarker performance/profiler runner server plugin ) VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport activeresource railties ) @@ -174,9 +173,6 @@ task :copy_dispatches do copy_with_rewritten_ruby_path("dispatches/dispatch.fcgi", "#{PKG_DESTINATION}/public/dispatch.fcgi") chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi" - - # copy_with_rewritten_ruby_path("dispatches/gateway.cgi", "#{PKG_DESTINATION}/public/gateway.cgi") - # chmod 0755, "#{PKG_DESTINATION}/public/gateway.cgi" end task :copy_html_files do diff --git a/railties/bin/process/inspector b/railties/bin/process/inspector deleted file mode 100755 index bf25ad86d1..0000000000 --- a/railties/bin/process/inspector +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/inspector' diff --git a/railties/bin/process/reaper b/railties/bin/process/reaper deleted file mode 100755 index c77f04535f..0000000000 --- a/railties/bin/process/reaper +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/reaper' diff --git a/railties/bin/process/spawner b/railties/bin/process/spawner deleted file mode 100755 index 7118f3983c..0000000000 --- a/railties/bin/process/spawner +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/spawner' diff --git a/railties/bin/rails b/railties/bin/rails index ae0cc8adca..6a0c675206 100755 --- a/railties/bin/rails +++ b/railties/bin/rails @@ -8,6 +8,7 @@ if %w(--version -v).include? ARGV.first end freeze = ARGV.any? { |option| %w(--freeze -f).include?(option) } + app_path = ARGV.first require File.dirname(__FILE__) + '/../lib/rails_generator' diff --git a/railties/config.ru b/railties/config.ru deleted file mode 100644 index 43492a2dcc..0000000000 --- a/railties/config.ru +++ /dev/null @@ -1,17 +0,0 @@ -# Rackup Configuration -# -# Start Rails mongrel server with rackup -# $ rackup -p 3000 config.ru -# -# Start server with webrick (or any compatible Rack server) instead -# $ rackup -p 3000 -s webrick config.ru - -# Require your environment file to bootstrap Rails -require File.dirname(__FILE__) + '/config/environment' - -# Static server middleware -# You can remove this extra check if you use an asset server -use Rails::Rack::Static - -# Dispatch the request -run ActionController::Dispatcher.new diff --git a/railties/configs/initializers/new_rails_defaults.rb b/railties/configs/initializers/new_rails_defaults.rb index 78e0117cc4..8ec3186c84 100644 --- a/railties/configs/initializers/new_rails_defaults.rb +++ b/railties/configs/initializers/new_rails_defaults.rb @@ -1,3 +1,5 @@ +# Be sure to restart your server when you modify this file. + # These settings change the behavior of Rails 2 apps and will be defaults # for Rails 3. You can remove this initializer when Rails 3 is released. diff --git a/railties/configs/initializers/session_store.rb b/railties/configs/initializers/session_store.rb new file mode 100644 index 0000000000..40179e0aa3 --- /dev/null +++ b/railties/configs/initializers/session_store.rb @@ -0,0 +1,15 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying cookie session data integrity. +# If you change this key, all old sessions will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +ActionController::Base.session = { + :session_key => '_<%= app_name %>_session', + :secret => '<%= app_secret %>' +} + +# Use the database for sessions instead of the cookie-based default, +# which shouldn't be used to store highly confidential information +# (create the session table with "rake db:sessions:create") +# ActionController::Base.session_store = :active_record_store diff --git a/railties/dispatches/config.ru b/railties/dispatches/config.ru new file mode 100644 index 0000000000..acbfe4e9ae --- /dev/null +++ b/railties/dispatches/config.ru @@ -0,0 +1,7 @@ +# Rack Dispatcher + +# Require your environment file to bootstrap Rails +require File.dirname(__FILE__) + '/config/environment' + +# Dispatch the request +run ActionController::Dispatcher.new diff --git a/railties/environments/environment.rb b/railties/environments/environment.rb index 5cb201401b..4a2df36307 100644 --- a/railties/environments/environment.rb +++ b/railties/environments/environment.rb @@ -1,9 +1,5 @@ # Be sure to restart your server when you modify this file -# Uncomment below to force Rails into production mode when -# you don't control web/app server and can't set it the proper way -# ENV['RAILS_ENV'] ||= 'production' - # Specifies gem version of Rails to use when vendor/rails is not present <%= '# ' if freeze %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>' unless defined? RAILS_GEM_VERSION @@ -14,62 +10,32 @@ Rails::Initializer.run do |config| # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. - # See Rails::Configuration for more options. - # Skip frameworks you're not going to use. To use Rails without a database - # you must remove the Active Record framework. - # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] + # Add additional load paths for your own custom dirs + # config.load_paths += %W( #{RAILS_ROOT}/extras ) - # Specify gems that this application depends on. - # They can then be installed with "rake gems:install" on new installations. - # You have to specify the :lib option for libraries, where the Gem name (sqlite3-ruby) differs from the file itself (sqlite3) + # Specify gems that this application depends on and have them installed with rake gems:install # config.gem "bj" # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" # config.gem "sqlite3-ruby", :lib => "sqlite3" # config.gem "aws-s3", :lib => "aws/s3" - # Only load the plugins named here, in the order given. By default, all plugins - # in vendor/plugins are loaded in alphabetical order. + # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named # config.plugins = [ :exception_notification, :ssl_requirement, :all ] - # Add additional load paths for your own custom dirs - # config.load_paths += %W( #{RAILS_ROOT}/extras ) + # Skip frameworks you're not going to use. To use Rails without a database, + # you must remove the Active Record framework. + # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] - # Force all environments to use the same logger level - # (by default production uses :info, the others :debug) - # config.log_level = :debug + # Activate observers that should always be running + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer - # Make Time.zone default to the specified zone, and make Active Record store time values - # in the database in UTC, and return them converted to the specified local zone. - # Run "rake -D time" for a list of tasks for finding time zone names. Comment line to use default local time. + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. config.time_zone = 'UTC' - # The internationalization framework can be changed to have another default locale (standard is :en) or more load paths. - # All files from config/locales/*.rb,yml are added automatically. - # config.i18n.load_path << Dir[File.join(RAILS_ROOT, 'my', 'locales', '*.{rb,yml}')] + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')] # config.i18n.default_locale = :de - - # Your secret key for verifying cookie session data integrity. - # If you change this key, all old sessions will become invalid! - # Make sure the secret is at least 30 characters and all random, - # no regular words or you'll be exposed to dictionary attacks. - config.action_controller.session = { - :session_key => '_<%= app_name %>_session', - :secret => '<%= app_secret %>' - } - - # Use the database for sessions instead of the cookie-based default, - # which shouldn't be used to store highly confidential information - # (create the session table with "rake db:sessions:create") - # config.action_controller.session_store = :active_record_store - - # Use SQL instead of Active Record's schema dumper when creating the test database. - # This is necessary if your schema can't be completely dumped by the schema dumper, - # like if you have constraints or database-specific column types - # config.active_record.schema_format = :sql - - # Activate observers that should always be running - # Please note that observers generated using script/generate observer need to have an _observer suffix - # config.active_record.observers = :cacher, :garbage_collector, :forum_observer -end +end
\ No newline at end of file diff --git a/railties/environments/production.rb b/railties/environments/production.rb index ec5b7bc865..1fc9f6b923 100644 --- a/railties/environments/production.rb +++ b/railties/environments/production.rb @@ -4,21 +4,24 @@ # Code is not reloaded between requests config.cache_classes = true -# Enable threaded mode -# config.threadsafe! - -# Use a different logger for distributed setups -# config.logger = SyslogLogger.new - # Full error reports are disabled and caching is turned on config.action_controller.consider_all_requests_local = false config.action_controller.perform_caching = true +# See everything in the log (default is :info) +# config.log_level = :debug + +# Use a different logger for distributed setups +# config.logger = SyslogLogger.new + # Use a different cache store in production # config.cache_store = :mem_cache_store # Enable serving of images, stylesheets, and javascripts from an asset server -# config.action_controller.asset_host = "http://assets.example.com" +# config.action_controller.asset_host = "http://assets.example.com" # Disable delivery errors, bad email addresses will be ignored # config.action_mailer.raise_delivery_errors = false + +# Enable threaded mode +# config.threadsafe!
\ No newline at end of file diff --git a/railties/environments/test.rb b/railties/environments/test.rb index 1e709e1d19..496eb9572b 100644 --- a/railties/environments/test.rb +++ b/railties/environments/test.rb @@ -20,3 +20,8 @@ config.action_controller.allow_forgery_protection = false # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + +# Use SQL instead of Active Record's schema dumper when creating the test database. +# This is necessary if your schema can't be completely dumped by the schema dumper, +# like if you have constraints or database-specific column types +# config.active_record.schema_format = :sql
\ No newline at end of file diff --git a/railties/helpers/application_controller.rb b/railties/helpers/application_controller.rb index ef33aa8353..6635a3f487 100644 --- a/railties/helpers/application_controller.rb +++ b/railties/helpers/application_controller.rb @@ -3,12 +3,8 @@ class ApplicationController < ActionController::Base helper :all # include all helpers, all the time + protect_from_forgery # See ActionController::RequestForgeryProtection for details - # See ActionController::RequestForgeryProtection for details - protect_from_forgery - - # See ActionController::Base for details - # Uncomment this to filter the contents of submitted sensitive data parameters - # from your application log (in this case, all fields with names like "password"). + # Scrub sensitive parameters from your log # filter_parameter_logging :password end diff --git a/railties/lib/commands/process/inspector.rb b/railties/lib/commands/process/inspector.rb deleted file mode 100644 index 8a6437e715..0000000000 --- a/railties/lib/commands/process/inspector.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'optparse' - -if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Inspector is only for Unix") end - -OPTIONS = { - :pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'), - :pattern => "dispatch.*.pid", - :ps => "ps -o pid,state,user,start,time,pcpu,vsz,majflt,command -p %s" -} - -class Inspector - def self.inspect(pid_path, pattern) - new(pid_path, pattern).inspect - end - - def initialize(pid_path, pattern) - @pid_path, @pattern = pid_path, pattern - end - - def inspect - header = `#{OPTIONS[:ps] % 1}`.split("\n")[0] + "\n" - lines = pids.collect { |pid| `#{OPTIONS[:ps] % pid}`.split("\n")[1] } - - puts(header + lines.join("\n")) - end - - private - def pids - pid_files.collect do |pid_file| - File.read(pid_file).to_i - end - end - - def pid_files - Dir.glob(@pid_path + "/" + @pattern) - end -end - - -ARGV.options do |opts| - opts.banner = "Usage: inspector [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - Displays system information about Rails dispatchers (or other processes that use pid files) through - the ps command. - - Examples: - inspector # default ps on all tmp/pids/dispatch.*.pid files - inspector -s 'ps -o user,start,majflt,pcpu,vsz -p %s' # custom ps, %s is where the pid is interleaved - EOF - - opts.on(" Options:") - - opts.on("-s", "--ps=command", "default: #{OPTIONS[:ps]}", String) { |v| OPTIONS[:ps] = v } - opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v } - opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -Inspector.inspect(OPTIONS[:pid_path], OPTIONS[:pattern]) diff --git a/railties/lib/commands/process/reaper.rb b/railties/lib/commands/process/reaper.rb deleted file mode 100644 index 95175d41e0..0000000000 --- a/railties/lib/commands/process/reaper.rb +++ /dev/null @@ -1,149 +0,0 @@ -require 'optparse' -require 'net/http' -require 'uri' - -if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Reaper is only for Unix") end - -class Killer - class << self - # Searches for all processes matching the given keywords, and then invokes - # a specific action on each of them. This is useful for (e.g.) reloading a - # set of processes: - # - # Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid") - def process(action, pid_path, pattern, keyword) - new(pid_path, pattern, keyword).process(action) - end - - # Forces the (rails) application to reload by sending a +HUP+ signal to the - # process. - def reload(pid) - `kill -s HUP #{pid}` - end - - # Force the (rails) application to restart by sending a +USR2+ signal to the - # process. - def restart(pid) - `kill -s USR2 #{pid}` - end - - # Forces the (rails) application to gracefully terminate by sending a - # +TERM+ signal to the process. - def graceful(pid) - `kill -s TERM #{pid}` - end - - # Forces the (rails) application to terminate immediately by sending a -9 - # signal to the process. - def kill(pid) - `kill -9 #{pid}` - end - - # Send a +USR1+ signal to the process. - def usr1(pid) - `kill -s USR1 #{pid}` - end - end - - def initialize(pid_path, pattern, keyword=nil) - @pid_path, @pattern, @keyword = pid_path, pattern, keyword - end - - def process(action) - pids = find_processes - - if pids.empty? - warn "Couldn't find any pid file in '#{@pid_path}' matching '#{@pattern}'" - warn "(also looked for processes matching #{@keyword.inspect})" if @keyword - else - pids.each do |pid| - puts "#{action.capitalize}ing #{pid}" - self.class.send(action, pid) - end - - delete_pid_files if terminating?(action) - end - end - - private - def terminating?(action) - [ "kill", "graceful" ].include?(action) - end - - def find_processes - files = pid_files - if files.empty? - find_processes_via_grep - else - files.collect { |pid_file| File.read(pid_file).to_i } - end - end - - def find_processes_via_grep - lines = `ps axww -o 'pid command' | grep #{@keyword}`.split(/\n/). - reject { |line| line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/ } - lines.map { |line| line[/^\s*(\d+)/, 1].to_i } - end - - def delete_pid_files - pid_files.each { |pid_file| File.delete(pid_file) } - end - - def pid_files - Dir.glob(@pid_path + "/" + @pattern) - end -end - - -OPTIONS = { - :action => "restart", - :pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'), - :pattern => "dispatch.[0-9]*.pid", - :dispatcher => File.expand_path("#{RAILS_ROOT}/public/dispatch.fcgi") -} - -ARGV.options do |opts| - opts.banner = "Usage: reaper [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - The reaper is used to restart, reload, gracefully exit, and forcefully exit processes - running a Rails Dispatcher (or any other process responding to the same signals). This - is commonly done when a new version of the application is available, so the existing - processes can be updated to use the latest code. - - It uses pid files to work on the processes and by default assume them to be located - in RAILS_ROOT/tmp/pids. - - The reaper actions are: - - * restart : Restarts the application by reloading both application and framework code - * reload : Only reloads the application, but not the framework (like the development environment) - * graceful: Marks all of the processes for exit after the next request - * kill : Forcefully exists all processes regardless of whether they're currently serving a request - - Restart is the most common and default action. - - Examples: - reaper # restarts the default dispatchers - reaper -a reload # reload the default dispatchers - reaper -a kill -r *.pid # kill all processes that keep pids in tmp/pids - EOF - - opts.on(" Options:") - - opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |v| OPTIONS[:action] = v } - opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v } - opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v } - opts.on("-d", "--dispatcher=path", "DEPRECATED. default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher]) diff --git a/railties/lib/commands/process/spawner.rb b/railties/lib/commands/process/spawner.rb deleted file mode 100644 index 8bf47abb75..0000000000 --- a/railties/lib/commands/process/spawner.rb +++ /dev/null @@ -1,219 +0,0 @@ -require 'active_support' -require 'optparse' -require 'socket' -require 'fileutils' - -def daemonize #:nodoc: - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. - Dir.chdir "/" # Release old working directory. - File.umask 0000 # Ensure sensible umask. Adjust as needed. - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile. -end - -class Spawner - def self.record_pid(name = "#{OPTIONS[:process]}.spawner", id = Process.pid) - FileUtils.mkdir_p(OPTIONS[:pids]) - File.open(File.expand_path(OPTIONS[:pids] + "/#{name}.pid"), "w+") { |f| f.write(id) } - end - - def self.spawn_all - OPTIONS[:instances].times do |i| - port = OPTIONS[:port] + i - print "Checking if something is already running on #{OPTIONS[:address]}:#{port}..." - - begin - srv = TCPServer.new(OPTIONS[:address], port) - srv.close - srv = nil - - puts "NO" - puts "Starting dispatcher on port: #{OPTIONS[:address]}:#{port}" - - FileUtils.mkdir_p(OPTIONS[:pids]) - spawn(port) - rescue - puts "YES" - end - end - end -end - -class FcgiSpawner < Spawner - def self.spawn(port) - cmd = "#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port} -P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid" - cmd << " -a #{OPTIONS[:address]}" if can_bind_to_custom_address? - system(cmd) - end - - def self.can_bind_to_custom_address? - @@can_bind_to_custom_address ||= /^\s-a\s/.match `#{OPTIONS[:spawner]} -h` - end -end - -class MongrelSpawner < Spawner - def self.spawn(port) - cmd = - "mongrel_rails start -d " + - "-a #{OPTIONS[:address]} " + - "-p #{port} " + - "-P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid " + - "-e #{OPTIONS[:environment]} " + - "-c #{OPTIONS[:rails_root]} " + - "-l #{OPTIONS[:rails_root]}/log/mongrel.log" - - # Add prefix functionality to spawner's call to mongrel_rails - # Digging through mongrel's project subversion server, the earliest - # Tag that has prefix implemented in the bin/mongrel_rails file - # is 0.3.15 which also happens to be the earliest tag listed. - # References: http://mongrel.rubyforge.org/svn/tags - if Mongrel::Const::MONGREL_VERSION.to_f >=0.3 && !OPTIONS[:prefix].nil? - cmd = cmd + " --prefix #{OPTIONS[:prefix]}" - end - system(cmd) - end - - def self.can_bind_to_custom_address? - true - end -end - - -begin - require_library_or_gem 'fcgi' -rescue Exception - # FCGI not available -end - -begin - require_library_or_gem 'mongrel' -rescue Exception - # Mongrel not available -end - -server = case ARGV.first - when "fcgi", "mongrel" - ARGV.shift - else - if defined?(Mongrel) - "mongrel" - elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `spawn-fcgi -version` }.blank? && defined?(FCGI) - "fcgi" - end -end - -case server - when "fcgi" - puts "=> Starting FCGI dispatchers" - spawner_class = FcgiSpawner - when "mongrel" - puts "=> Starting mongrel dispatchers" - spawner_class = MongrelSpawner - else - puts "Neither FCGI (spawn-fcgi) nor Mongrel was installed and available!" - exit(0) -end - - - -OPTIONS = { - :environment => "production", - :spawner => '/usr/bin/env spawn-fcgi', - :dispatcher => File.expand_path(RELATIVE_RAILS_ROOT + '/public/dispatch.fcgi'), - :pids => File.expand_path(RELATIVE_RAILS_ROOT + "/tmp/pids"), - :rails_root => File.expand_path(RELATIVE_RAILS_ROOT), - :process => "dispatch", - :port => 8000, - :address => '0.0.0.0', - :instances => 3, - :repeat => nil, - :prefix => nil -} - -ARGV.options do |opts| - opts.banner = "Usage: spawner [platform] [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - The spawner is a wrapper for spawn-fcgi and mongrel that makes it - easier to start multiple processes running the Rails dispatcher. The - spawn-fcgi command is included with the lighttpd web server, but can - be used with both Apache and lighttpd (and any other web server - supporting externally managed FCGI processes). Mongrel automatically - ships with with mongrel_rails for starting dispatchers. - - The first choice you need to make is whether to spawn the Rails - dispatchers as FCGI or Mongrel. By default, this spawner will prefer - Mongrel, so if that's installed, and no platform choice is made, - Mongrel is used. - - Then decide a starting port (default is 8000) and the number of FCGI - process instances you'd like to run. So if you pick 9100 and 3 - instances, you'll start processes on 9100, 9101, and 9102. - - By setting the repeat option, you get a protection loop, which will - attempt to restart any FCGI processes that might have been exited or - outright crashed. - - You can select bind address for started processes. By default these - listen on every interface. For single machine installations you would - probably want to use 127.0.0.1, hiding them form the outside world. - - Examples: - spawner # starts instances on 8000, 8001, and 8002 - # using Mongrel if available. - spawner fcgi # starts instances on 8000, 8001, and 8002 - # using FCGI. - spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, - # 8003, and 8004 using Mongrel. - spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to - # 9109 using Mongrel if available. - spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to - # 9102 and attempts start them every 5 - # seconds. - spawner -a 127.0.0.1 # starts 3 instances binding to localhost - EOF - - opts.on(" Options:") - - opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v } - - if spawner_class.can_bind_to_custom_address? - opts.on("-a", "--address=ip", String, "Bind to IP address (default: #{OPTIONS[:address]})") { |v| OPTIONS[:address] = v } - end - - opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v } - opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |v| OPTIONS[:instances] = v } - opts.on("-r", "--repeat=seconds", Integer, "Repeat spawn attempts every n seconds (default: off)") { |v| OPTIONS[:repeat] = v } - opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |v| OPTIONS[:environment] = v } - opts.on("-P", "--prefix=path", String, "URL prefix for Rails app. [Used only with Mongrel > v0.3.15]: (default: #{OPTIONS[:prefix]})") { |v| OPTIONS[:prefix] = v } - opts.on("-n", "--process=name", String, "default: #{OPTIONS[:process]}") { |v| OPTIONS[:process] = v } - opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |v| OPTIONS[:spawner] = v } - opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -ENV["RAILS_ENV"] = OPTIONS[:environment] - -if OPTIONS[:repeat] - daemonize - trap("TERM") { exit } - spawner_class.record_pid - - loop do - spawner_class.spawn_all - sleep(OPTIONS[:repeat]) - end -else - spawner_class.spawn_all -end diff --git a/railties/lib/commands/process/spinner.rb b/railties/lib/commands/process/spinner.rb deleted file mode 100644 index c0b2f09a94..0000000000 --- a/railties/lib/commands/process/spinner.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'optparse' - -def daemonize #:nodoc: - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. - Dir.chdir "/" # Release old working directory. - File.umask 0000 # Ensure sensible umask. Adjust as needed. - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile. -end - -OPTIONS = { - :interval => 5.0, - :command => File.expand_path(RAILS_ROOT + '/script/process/spawner'), - :daemon => false -} - -ARGV.options do |opts| - opts.banner = "Usage: spinner [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - The spinner is a protection loop for the spawner, which will attempt to restart any FCGI processes - that might have been exited or outright crashed. It's a brute-force attempt that'll just try - to run the spawner every X number of seconds, so it does pose a light load on the server. - - Examples: - spinner # attempts to run the spawner with default settings every second with output on the terminal - spinner -i 3 -d # only run the spawner every 3 seconds and detach from the terminal to become a daemon - spinner -c '/path/to/app/script/process/spawner -p 9000 -i 10' -d # using custom spawner - EOF - - opts.on(" Options:") - - opts.on("-c", "--command=path", String) { |v| OPTIONS[:command] = v } - opts.on("-i", "--interval=seconds", Float) { |v| OPTIONS[:interval] = v } - opts.on("-d", "--daemon") { |v| OPTIONS[:daemon] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -daemonize if OPTIONS[:daemon] - -trap(OPTIONS[:daemon] ? "TERM" : "INT") { exit } - -loop do - system(OPTIONS[:command]) - sleep(OPTIONS[:interval]) -end
\ No newline at end of file diff --git a/railties/lib/commands/server.rb b/railties/lib/commands/server.rb index a4bb52592f..7057fcc33f 100644 --- a/railties/lib/commands/server.rb +++ b/railties/lib/commands/server.rb @@ -65,7 +65,6 @@ end ENV["RAILS_ENV"] = options[:environment] RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) -require RAILS_ROOT + "/config/environment" if File.exist?(options[:config]) config = options[:config] @@ -74,20 +73,23 @@ if File.exist?(options[:config]) if cfgfile[/^#\\(.*)/] opts.parse!($1.split(/\s+/)) end - app = eval("Rack::Builder.new {( " + cfgfile + "\n )}.to_app", nil, config) + inner_app = eval("Rack::Builder.new {( " + cfgfile + "\n )}.to_app", nil, config) else require config - app = Object.const_get(File.basename(config, '.rb').capitalize) + inner_app = Object.const_get(File.basename(config, '.rb').capitalize) end else - app = Rack::Builder.new { - use Rails::Rack::Logger - use Rails::Rack::Static - use Rails::Rack::Debugger if options[:debugger] - run ActionController::Dispatcher.new - }.to_app + require RAILS_ROOT + "/config/environment" + inner_app = ActionController::Dispatcher.new end +app = Rack::Builder.new { + use Rails::Rack::Logger + use Rails::Rack::Static + use Rails::Rack::Debugger if options[:debugger] + run inner_app +}.to_app + puts "=> Call with -d to detach" trap(:INT) { exit } diff --git a/railties/lib/fcgi_handler.rb b/railties/lib/fcgi_handler.rb index 1bb55b9275..1256ef2286 100644 --- a/railties/lib/fcgi_handler.rb +++ b/railties/lib/fcgi_handler.rb @@ -98,7 +98,7 @@ class RailsFCGIHandler with_signal_handler 'USR1' do begin - Dispatcher.dispatch(cgi) + ::Rack::Handler::FastCGI.serve(cgi, Dispatcher.new) rescue SignalException, SystemExit raise rescue Exception => error diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index b645ac3da2..4bb1e480b7 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -48,8 +48,8 @@ module Rails end end - def root(*args) - File.join(RAILS_ROOT, *args.compact) if defined?(RAILS_ROOT) + def root + Pathname.new(RAILS_ROOT) if defined?(RAILS_ROOT) end def env @@ -513,10 +513,15 @@ Run `rake gems:install` to install the missing gems. def initialize_time_zone if configuration.time_zone zone_default = Time.__send__(:get_zone, configuration.time_zone) + unless zone_default - raise %{Value assigned to config.time_zone not recognized. Run "rake -D time" for a list of tasks for finding appropriate time zone names.} + raise \ + 'Value assigned to config.time_zone not recognized.' + + 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' end + Time.zone_default = zone_default + if configuration.frameworks.include?(:active_record) ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord::Base.default_timezone = :utc @@ -876,6 +881,11 @@ Run `rake gems:install` to install the missing gems. end end + def middleware + require 'action_controller' + ActionController::Dispatcher.middleware + end + def builtin_directories # Include builtins only in the development environment. (environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : [] diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index cd280ac023..5a07841be8 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -74,6 +74,7 @@ module Rails def dependencies return [] if framework_gem? + return [] if specification.nil? all_dependencies = specification.dependencies.map do |dependency| GemDependency.new(dependency.name, :requirement => dependency.version_requirements) end diff --git a/railties/lib/rails_generator/base.rb b/railties/lib/rails_generator/base.rb index b5cfe79867..aa7081f8da 100644 --- a/railties/lib/rails_generator/base.rb +++ b/railties/lib/rails_generator/base.rb @@ -154,6 +154,9 @@ module Rails File.join(destination_root, relative_destination) end + def after_generate + end + protected # Convenience method for generator subclasses to record a manifest. def record diff --git a/railties/lib/rails_generator/commands.rb b/railties/lib/rails_generator/commands.rb index 6b9a636847..cacb3807d6 100644 --- a/railties/lib/rails_generator/commands.rb +++ b/railties/lib/rails_generator/commands.rb @@ -40,6 +40,7 @@ module Rails # Replay action manifest. RewindBase subclass rewinds manifest. def invoke! manifest.replay(self) + after_generate end def dependency(generator_name, args, runtime_options = {}) diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb index 32383d2bbd..4a191578cf 100644 --- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb +++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -1,117 +1,46 @@ require 'rbconfig' +require File.dirname(__FILE__) + '/template_runner' require 'digest/md5' require 'active_support/secure_random' class AppGenerator < Rails::Generator::Base - DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], - Config::CONFIG['ruby_install_name']) + DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) - DATABASES = %w(mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db) + DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) DEFAULT_DATABASE = 'sqlite3' + mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.." default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE), :shebang => DEFAULT_SHEBANG, :with_dispatchers => false, :freeze => false - mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.." + def initialize(runtime_args, runtime_options = {}) super + usage if args.empty? usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db])) + @destination_root = args.shift @app_name = File.basename(File.expand_path(@destination_root)) end def manifest - # Use /usr/bin/env if no special shebang was specified - script_options = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] } - dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] } - - # duplicate CGI::Session#generate_unique_id - md5 = Digest::MD5.new - now = Time.now - md5 << now.to_s - md5 << String(now.usec) - md5 << String(rand(0)) - md5 << String($$) - md5 << @app_name - - # Do our best to generate a secure secret key for CookieStore - secret = ActiveSupport::SecureRandom.hex(64) - record do |m| - # Root directory and all subdirectories. - m.directory '' - BASEDIRS.each { |path| m.directory path } - - # Root - m.file "fresh_rakefile", "Rakefile" - m.file "README", "README" - - # Application - m.template "helpers/application_controller.rb", "app/controllers/application_controller.rb", :assigns => { - :app_name => @app_name, :app_secret => md5.hexdigest } - m.template "helpers/application_helper.rb", "app/helpers/application_helper.rb" - m.template "helpers/test_helper.rb", "test/test_helper.rb" - m.template "helpers/performance_test.rb", "test/performance/browsing_test.rb" - - # database.yml and routes.rb - m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => { - :app_name => @app_name, - :socket => options[:db] == "mysql" ? mysql_socket_location : nil - } - m.template "configs/routes.rb", "config/routes.rb" - - # Initializers - m.template "configs/initializers/backtrace_silencers.rb", "config/initializers/backtrace_silencers.rb" - m.template "configs/initializers/inflections.rb", "config/initializers/inflections.rb" - m.template "configs/initializers/mime_types.rb", "config/initializers/mime_types.rb" - m.template "configs/initializers/new_rails_defaults.rb", "config/initializers/new_rails_defaults.rb" - - # Locale - m.template "configs/locales/en.yml", "config/locales/en.yml" - - # Environments - m.file "environments/boot.rb", "config/boot.rb" - m.template "environments/environment.rb", "config/environment.rb", :assigns => { :freeze => options[:freeze], :app_name => @app_name, :app_secret => secret } - m.file "environments/production.rb", "config/environments/production.rb" - m.file "environments/development.rb", "config/environments/development.rb" - m.file "environments/test.rb", "config/environments/test.rb" - - # Scripts - %w( about console dbconsole destroy generate performance/benchmarker performance/profiler performance/request process/reaper process/spawner process/inspector runner server plugin ).each do |file| - m.file "bin/#{file}", "script/#{file}", script_options - end - - # Dispatches - if options[:with_dispatchers] - m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options - m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options - m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options - end - - # HTML files - %w(404 422 500 index).each do |file| - m.template "html/#{file}.html", "public/#{file}.html" - end - - m.template "html/favicon.ico", "public/favicon.ico" - m.template "html/robots.txt", "public/robots.txt" - m.file "html/images/rails.png", "public/images/rails.png" - - # Javascripts - m.file "html/javascripts/prototype.js", "public/javascripts/prototype.js" - m.file "html/javascripts/effects.js", "public/javascripts/effects.js" - m.file "html/javascripts/dragdrop.js", "public/javascripts/dragdrop.js" - m.file "html/javascripts/controls.js", "public/javascripts/controls.js" - m.file "html/javascripts/application.js", "public/javascripts/application.js" - - # Docs - m.file "doc/README_FOR_APP", "doc/README_FOR_APP" + create_directories(m) + create_root_files(m) + create_app_files(m) + create_config_files(m) + create_script_files(m) + create_test_files(m) + create_public_files(m) + create_documentation_file(m) + create_log_files(m) + end + end - # Logs - %w(server production development test).each { |file| - m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666 - } + def after_generate + if options[:template] + Rails::TemplateRunner.new(@destination_root, options[:template]) end end @@ -138,55 +67,192 @@ class AppGenerator < Rails::Generator::Base opt.on("-f", "--freeze", "Freeze Rails in vendor/rails from the gems generating the skeleton", "Default: false") { |v| options[:freeze] = v } + + opt.on("-m", "--template=path", String, + "Use an application template that lives at path (can be a filesystem path or URL).", + "Default: (none)") { |v| options[:template] = v } + + end + + + private + def create_directories(m) + m.directory '' + + # Intermediate directories are automatically created so don't sweat their absence here. + %w( + app/controllers + app/helpers + app/models + app/views/layouts + config/environments + config/initializers + config/locales + db + doc + lib + lib/tasks + log + public/images + public/javascripts + public/stylesheets + script/performance + test/fixtures + test/functional + test/integration + test/performance + test/unit + vendor + vendor/plugins + tmp/sessions + tmp/sockets + tmp/cache + tmp/pids + ).each { |path| m.directory(path) } + end + + def create_root_files(m) + m.file "fresh_rakefile", "Rakefile" + m.file "README", "README" + end + + def create_app_files(m) + m.file "helpers/application_controller.rb", "app/controllers/application_controller.rb" + m.file "helpers/application_helper.rb", "app/helpers/application_helper.rb" + end + + def create_config_files(m) + create_database_configuration_file(m) + create_routes_file(m) + create_locale_file(m) + create_initializer_files(m) + create_environment_files(m) + end + + def create_documentation_file(m) + m.file "doc/README_FOR_APP", "doc/README_FOR_APP" end + def create_log_files(m) + %w( server production development test ).each do |file| + m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666 + end + end + + def create_public_files(m) + create_dispatch_files(m) + create_error_files(m) + create_welcome_file(m) + create_browser_convention_files(m) + create_rails_image(m) + create_javascript_files(m) + end + + def create_script_files(m) + %w( + about console dbconsole destroy generate runner server plugin + performance/benchmarker performance/profiler performance/request + ).each do |file| + m.file "bin/#{file}", "script/#{file}", { + :chmod => 0755, + :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] + } + end + end + + def create_test_files(m) + m.file "helpers/test_helper.rb", "test/test_helper.rb" + m.file "helpers/performance_test.rb", "test/performance/browsing_test.rb" + end + + + def create_database_configuration_file(m) + m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => { + :app_name => @app_name, + :socket => options[:db] == "mysql" ? mysql_socket_location : nil } + end + + def create_routes_file(m) + m.file "configs/routes.rb", "config/routes.rb" + end + + def create_initializer_files(m) + %w( + backtrace_silencers + inflections + mime_types + new_rails_defaults + ).each do |initializer| + m.file "configs/initializers/#{initializer}.rb", "config/initializers/#{initializer}.rb" + end + + m.template "configs/initializers/session_store.rb", "config/initializers/session_store.rb", + :assigns => { :app_name => @app_name, :app_secret => ActiveSupport::SecureRandom.hex(64) } + end + + def create_locale_file(m) + m.file "configs/locales/en.yml", "config/locales/en.yml" + end + + def create_environment_files(m) + m.template "environments/environment.rb", "config/environment.rb", + :assigns => { :freeze => options[:freeze] } + + m.file "environments/boot.rb", "config/boot.rb" + m.file "environments/production.rb", "config/environments/production.rb" + m.file "environments/development.rb", "config/environments/development.rb" + m.file "environments/test.rb", "config/environments/test.rb" + end + + + def create_dispatch_files(m) + if options[:with_dispatchers] + dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] } + + m.file "dispatches/config.ru", "config.ru" + m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options + m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options + m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options + end + end + + def create_error_files(m) + %w( 404 422 500 ).each do |file| + m.file "html/#{file}.html", "public/#{file}.html" + end + end + + def create_welcome_file(m) + m.file 'html/index.html', 'public/index.html' + end + + def create_browser_convention_files(m) + m.file "html/favicon.ico", "public/favicon.ico" + m.file "html/robots.txt", "public/robots.txt" + end + + def create_rails_image(m) + m.file "html/images/rails.png", "public/images/rails.png" + end + + def create_javascript_files(m) + %w( prototype effects dragdrop controls application ).each do |javascript| + m.file "html/javascripts/#{javascript}.js", "public/javascripts/#{javascript}.js" + end + end + + def mysql_socket_location - MYSQL_SOCKET_LOCATIONS.find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ - end - - - # Installation skeleton. Intermediate directories are automatically - # created so don't sweat their absence here. - BASEDIRS = %w( - app/controllers - app/helpers - app/models - app/views/layouts - config/environments - config/initializers - config/locales - db - doc - lib - lib/tasks - log - public/images - public/javascripts - public/stylesheets - script/performance - script/process - test/fixtures - test/functional - test/integration - test/performance - test/unit - vendor - vendor/plugins - tmp/sessions - tmp/sockets - tmp/cache - tmp/pids - ) - - MYSQL_SOCKET_LOCATIONS = [ - "/tmp/mysql.sock", # default - "/var/run/mysqld/mysqld.sock", # debian/gentoo - "/var/tmp/mysql.sock", # freebsd - "/var/lib/mysql/mysql.sock", # fedora - "/opt/local/lib/mysql/mysql.sock", # fedora - "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql - "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 - "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 - "/opt/lampp/var/mysql/mysql.sock" # xampp for linux - ] -end + [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/git.rb b/railties/lib/rails_generator/generators/applications/app/scm/git.rb new file mode 100644 index 0000000000..445de6ab42 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/scm/git.rb @@ -0,0 +1,16 @@ +module Rails + class Git < Scm + def self.clone(repos, branch=nil) + `git clone #{repos}` + + if branch + `cd #{repos.split('/').last}/` + `git checkout #{branch}` + end + end + + def self.run(command) + `git #{command}` + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/scm.rb b/railties/lib/rails_generator/generators/applications/app/scm/scm.rb new file mode 100644 index 0000000000..f6c08cad39 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/scm/scm.rb @@ -0,0 +1,8 @@ +module Rails + class Scm + private + def self.hash_to_parameters(hash) + hash.collect { |key, value| "--#{key} #{(value.kind_of?(String) ? value : "")}"}.join(" ") + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/svn.rb b/railties/lib/rails_generator/generators/applications/app/scm/svn.rb new file mode 100644 index 0000000000..22b5966d25 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/scm/svn.rb @@ -0,0 +1,7 @@ +module Rails + class Svn < Scm + def self.checkout(repos, branch = nil) + `svn checkout #{repos}/#{branch || "trunk"}` + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/railties/lib/rails_generator/generators/applications/app/template_runner.rb new file mode 100644 index 0000000000..0083e0d5a5 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/template_runner.rb @@ -0,0 +1,363 @@ +require File.dirname(__FILE__) + '/scm/scm' +require File.dirname(__FILE__) + '/scm/git' +require File.dirname(__FILE__) + '/scm/svn' + +require 'open-uri' +require 'fileutils' + +module Rails + class TemplateRunner + attr_reader :behavior, :description, :root + + def initialize(root, template) # :nodoc: + @root = Dir.pwd + "/" + root + + puts "applying template: #{template}" + + load_template(template) + + puts "#{template} applied." + end + + def load_template(template) + begin + code = open(template).read + in_root { self.instance_eval(code) } + rescue LoadError + raise "The template [#{template}] could not be loaded." + end + end + + # Create a new file in the Rails project folder. Specify the + # relative path from RAILS_ROOT. Data is the return value of a block + # or a data string. + # + # ==== Examples + # + # file("lib/fun_party.rb") do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # file("config/apach.conf", "your apache config") + # + def file(filename, data = nil, &block) + puts "creating file #{filename}" + dir, file = [File.dirname(filename), File.basename(filename)] + + inside(dir) do + File.open(file, "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Install a plugin. You must provide either a Subversion url or Git url. + # + # ==== Examples + # + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' + # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + # + def plugin(name, options) + puts "installing plugin #{name}" + + if options[:git] || options[:svn] + in_root do + `script/plugin install #{options[:svn] || options[:git]}` + end + else + puts "! no git or svn provided for #{name}. skipping..." + end + end + + # Adds an entry into config/environment.rb for the supplied gem : + def gem(name, options = {}) + puts "adding gem #{name}" + + sentinel = 'Rails::Initializer.run do |config|' + gems_code = "config.gem '#{name}'" + + if options.any? + opts = options.inject([]) {|result, h| result << [":#{h[0]} => '#{h[1]}'"] }.join(", ") + gems_code << ", #{opts}" + end + + in_root do + gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n #{gems_code}" + end + end + end + + # Run a command in git. + # + # ==== Examples + # + # git :init + # git :add => "this.file that.rb" + # git :add => "onefile.rb", :rm => "badfile.cxx" + # + def git(command = {}) + puts "running git #{command}" + + in_root do + if command.is_a?(Symbol) + Git.run(command.to_s) + else + command.each do |command, options| + Git.run("#{command} #{options}") + end + end + end + end + + # Create a new file in the vendor/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # vendor("sekrit.rb") do + # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" + # "salt = '#{sekrit_salt}'" + # end + # + # vendor("foreign.rb", "# Foreign code is fun") + # + def vendor(filename, data = nil, &block) + puts "vendoring file #{filename}" + inside("vendor") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Create a new file in the lib/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # lib("crypto.rb") do + # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" + # end + # + # lib("foreign.rb", "# Foreign code is fun") + # + def lib(filename, data = nil) + puts "add lib file #{filename}" + inside("lib") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Create a new Rakefile with the provided code (either in a block or a string). + # + # ==== Examples + # + # rakefile("bootstrap.rake") do + # project = ask("What is the UNIX name of your project?") + # + # <<-TASK + # namespace :#{project} do + # task :bootstrap do + # puts "i like boots!" + # end + # end + # TASK + # end + # + # rakefile("seed.rake", "puts 'im plantin ur seedz'") + # + def rakefile(filename, data = nil, &block) + puts "adding rakefile #{filename}" + inside("lib/tasks") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Create a new initializer with the provided code (either in a block or a string). + # + # ==== Examples + # + # initializer("globals.rb") do + # data = "" + # + # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do + # data << "#{const} = :entp" + # end + # + # data + # end + # + # initializer("api.rb", "API_KEY = '123456'") + # + def initializer(filename, data = nil, &block) + puts "adding initializer #{filename}" + inside("config/initializers") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Generate something using a generator from Rails or a plugin. + # The second parameter is the argument string that is passed to + # the generator or an Array that is joined. + # + # ==== Example + # + # generate(:authenticated, "user session") + # + def generate(what, args = nil) + puts "generating #{what}" + args = args.join(" ") if args.class == Array + + in_root { `#{root}/script/generate #{what} #{args}` } + end + + # Executes a command + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails) + # end + # + def run(command) + puts "executing #{command} from #{Dir.pwd}" + `#{command}` + end + + # Runs the supplied rake task + # + # ==== Example + # + # rake("db:migrate") + # rake("db:migrate", "production") + # + def rake(command, env = 'development') + puts "running rake task #{command}" + in_root { `rake #{command} RAILS_ENV=#{env}` } + end + + # Just run the capify command in root + # + # ==== Example + # + # capify! + # + def capify! + in_root { `capify .` } + end + + # Add Rails to /vendor/rails + # + # ==== Example + # + # freeze! + # + def freeze!(args = {}) + puts "vendoring rails edge" + in_root { `rake rails:freeze:edge` } + end + + # Make an entry in Rails routing file conifg/routes.rb + # + # === Example + # + # route "map.root :controller => :welcome" + # + def route(routing_code) + sentinel = 'ActionController::Routing::Routes.draw do |map|' + + in_root do + gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n #{routing_code}\n" + end + end + end + + protected + + # Get a user's input + # + # ==== Example + # + # answer = ask("Should I freeze the latest Rails?") + # freeze! if ask("Should I freeze the latest Rails?") == "yes" + # + def ask(string) + puts string + gets.strip + end + + # Do something in the root of the Rails application or + # a provided subfolder; the full path is yielded to the block you provide. + # The path is set back to the previous path when the method exits. + def inside(dir = '', &block) + folder = File.join(root, dir) + FileUtils.mkdir_p(folder) unless File.exist?(folder) + FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield } + end + + def in_root + FileUtils.cd(root) { yield } + end + + # Helper to test if the user says yes(y)? + # + # ==== Example + # + # freeze! if yes?("Should I freeze the latest Rails?") + # + def yes?(question) + answer = ask(question).downcase + answer == "y" || answer == "yes" + end + + # Helper to test if the user does NOT say yes(y)? + # + # ==== Example + # + # capify! if no?("Will you be using vlad to deploy your application?") + # + def no?(question) + !yes?(question) + end + + def gsub_file(relative_destination, regexp, *args, &block) + path = destination_path(relative_destination) + content = File.read(path).gsub(regexp, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + + def destination_path(relative_destination) + File.join(root, relative_destination) + end + end +end
\ No newline at end of file diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 5cb27f1f10..a90c1d4a77 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -1,7 +1,12 @@ namespace :db do + task :load_config => :rails_env do + require 'active_record' + ActiveRecord::Base.configurations = Rails::Configuration.new.database_configuration + end + namespace :create do desc 'Create all the local databases defined in config/database.yml' - task :all => :environment do + task :all => :load_config do ActiveRecord::Base.configurations.each_value do |config| # Skip entries that don't have a database key, such as the first entry here: # @@ -22,7 +27,7 @@ namespace :db do end desc 'Create the database defined in config/database.yml for the current RAILS_ENV' - task :create => :environment do + task :create => :load_config do create_database(ActiveRecord::Base.configurations[RAILS_ENV]) end @@ -76,7 +81,7 @@ namespace :db do namespace :drop do desc 'Drops all the local databases defined in config/database.yml' - task :all => :environment do + task :all => :load_config do ActiveRecord::Base.configurations.each_value do |config| # Skip entries that don't have a database key next unless config['database'] @@ -87,7 +92,7 @@ namespace :db do end desc 'Drops the database for the current RAILS_ENV' - task :drop => :environment do + task :drop => :load_config do config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] begin drop_database(config) @@ -393,6 +398,7 @@ end def drop_database(config) case config['adapter'] when 'mysql' + ActiveRecord::Base.establish_connection(config) ActiveRecord::Base.connection.drop_database config['database'] when /^sqlite/ FileUtils.rm(File.join(RAILS_ROOT, config['database'])) diff --git a/railties/lib/tasks/framework.rake b/railties/lib/tasks/framework.rake index df080e94ac..d639214ffe 100644 --- a/railties/lib/tasks/framework.rake +++ b/railties/lib/tasks/framework.rake @@ -128,6 +128,7 @@ namespace :rails do desc "Generate dispatcher files in RAILS_ROOT/public" task :generate_dispatchers do require 'railties_path' + FileUtils.cp(RAILTIES_PATH + '/dispatches/config.ru', RAILS_ROOT + '/config.ru') FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.fcgi', RAILS_ROOT + '/public/dispatch.fcgi') FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.rb') FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.cgi') diff --git a/railties/lib/tasks/middleware.rake b/railties/lib/tasks/middleware.rake new file mode 100644 index 0000000000..e0dcf50307 --- /dev/null +++ b/railties/lib/tasks/middleware.rake @@ -0,0 +1,7 @@ +desc 'Prints out your Rack middleware stack' +task :middleware => :environment do + ActionController::Dispatcher.middleware.each do |middleware| + puts "use #{middleware.inspect}" + end + puts "run ActionController::Dispatcher.new" +end diff --git a/railties/lib/tasks/misc.rake b/railties/lib/tasks/misc.rake index 5c99725203..411750bf40 100644 --- a/railties/lib/tasks/misc.rake +++ b/railties/lib/tasks/misc.rake @@ -3,6 +3,12 @@ task :environment do require(File.join(RAILS_ROOT, 'config', 'environment')) end +task :rails_env do + unless defined? RAILS_ENV + RAILS_ENV = ENV['RAILS_ENV'] ||= 'development' + end +end + desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.' task :secret do puts ActiveSupport::SecureRandom.hex(64) diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 4f9e824c9f..1d4f2b18b3 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -129,5 +129,19 @@ uses_mocha "Plugin Tests" do assert_equal '1.0.0', DUMMY_GEM_E_VERSION end + def test_gem_handle_missing_dependencies + dummy_gem = Rails::GemDependency.new "dummy-gem-g" + dummy_gem.add_load_paths + dummy_gem.load + assert dummy_gem.loaded? + debugger + assert_equal 2, dummy_gem.dependencies.size + assert_nothing_raised do + dummy_gem.dependencies.each do |g| + g.dependencies + end + end + end + end end diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index 33c81bc5ad..dad9e55e61 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -314,10 +314,10 @@ end class RailsRootTest < Test::Unit::TestCase def test_rails_dot_root_equals_rails_root - assert_equal RAILS_ROOT, Rails.root + assert_equal RAILS_ROOT, Rails.root.to_s end - def test_rails_dot_root_accepts_arguments_for_file_dot_join - assert_equal File.join(RAILS_ROOT, 'app', 'controllers'), Rails.root('app', 'controllers') + def test_rails_dot_root_should_be_a_pathname + assert_equal File.join(RAILS_ROOT, 'app', 'controllers'), Rails.root.join('app', 'controllers').to_s end end
\ No newline at end of file diff --git a/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification new file mode 100644 index 0000000000..70a36b9a8c --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification @@ -0,0 +1,39 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-f +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: absolutely-no-such-gem + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-f.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem F diff --git a/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb b/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb new file mode 100644 index 0000000000..0271c8c48a --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb @@ -0,0 +1 @@ +DUMMY_GEM_F_VERSION="1.0.0" diff --git a/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification new file mode 100644 index 0000000000..5483048c1c --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification @@ -0,0 +1,39 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-g +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-f + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-g.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb b/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb new file mode 100644 index 0000000000..8fc056586c --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb @@ -0,0 +1 @@ +DUMMY_GEM_G_VERSION="1.0.0" |