diff options
53 files changed, 303 insertions, 906 deletions
@@ -35,7 +35,7 @@ platforms :ruby do group :db do gem "pg", ">= 0.9.0" gem "mysql", ">= 2.8.1" - gem "mysql2", :git => 'git://github.com/brianmario/mysql2.git' + gem "mysql2", ">= 0.2.3" end end diff --git a/RAILS_VERSION b/RAILS_VERSION index 7706a9b5ac..30afce968e 100644 --- a/RAILS_VERSION +++ b/RAILS_VERSION @@ -1 +1 @@ -3.0.0.rc +3.1.0.beta diff --git a/README.rdoc b/README.rdoc index b1a9e961f9..ea98d78a27 100644 --- a/README.rdoc +++ b/README.rdoc @@ -14,16 +14,16 @@ and directing data to the view. In \Rails, the model is handled by what's called an object-relational mapping layer entitled Active Record. This layer allows you to present the data from database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in -link:files/vendor/rails/activerecord/README.html. +methods. You can read more about Active Record in its +{README}[link:files/activerecord/README_rdoc.html]. The controller and view are handled by the Action Pack, which handles both layers by its two parts: Action View and Action Controller. These two layers are bundled in a single package due to their heavy interdependence. This is unlike the relationship between the Active Record and Action Pack that is much more separate. Each of these packages can be used independently outside of -\Rails. You can read more about Action Pack in -link:files/vendor/rails/actionpack/README.html. +\Rails. You can read more about Action Pack in its +{README}[link:files/actionpack/README_rdoc.html]. == Getting Started @@ -58,7 +58,7 @@ the following resources handy: == Contributing -We encourage you to contribute to Ruby on \Rails! Please check out the {Contributing to \Rails +We encourage you to contribute to Ruby on \Rails! Please check out the {Contributing to Rails guide}[http://edgeguides.rubyonrails.org/contributing_to_rails.html] for guidelines about how to proceed. {Join us}[http://contributors.rubyonrails.org]! diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 805c89be1d..7dd3d401b1 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -1,9 +1,9 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index dce2e30d3f..66141c1de7 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,8 @@ +*Support routing constraints in functional tests. [Andrew White] + +*Add a header that tells Internet Explorer (all versions) to use the best available standards support. [Yehuda Katz] + + *Rails 3.0.0 [release candidate] (July 26th, 2010)* * Allow stylesheet/javascript extensions to be changed through railties. [Josh Kalderimis] diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index a5ebc18e2a..8fd77aeb17 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |s| s.add_dependency('i18n', '~> 0.4.1') s.add_dependency('rack', '~> 1.2.1') s.add_dependency('rack-test', '~> 0.5.4') - s.add_dependency('rack-mount', '~> 0.6.10') + s.add_dependency('rack-mount', '~> 0.6.12') s.add_dependency('tzinfo', '~> 0.3.23') s.add_dependency('erubis', '~> 2.6.6') end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index e02fe202e1..75ea6523f7 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -167,6 +167,7 @@ module ActionController @formats = nil @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } + @symbolized_path_params = nil @method = @request_method = nil @fullpath = @ip = @remote_ip = nil @env['action_dispatch.request.query_parameters'] = {} diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index 5e1a2405f7..ef5d207b26 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -15,14 +15,14 @@ module ActionDispatch alias :params :parameters def path_parameters=(parameters) #:nodoc: - @_symbolized_path_params = nil + @symbolized_path_params = nil @env.delete("action_dispatch.request.parameters") @env["action_dispatch.request.path_parameters"] = parameters end # The same as <tt>path_parameters</tt> with explicitly symbolized keys. def symbolized_path_parameters - @_symbolized_path_params ||= path_parameters.symbolize_keys + @symbolized_path_params ||= path_parameters.symbolize_keys end # Returns a hash with the \parameters used to form the \path of the request. diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 23b13d1d5d..9aa34e7ba5 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -43,9 +43,10 @@ module ActionDispatch class Mapping #:nodoc: IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix] - def initialize(set, scope, args) - @set, @scope = set, scope - @path, @options = extract_path_and_options(args) + def initialize(set, scope, path, options) + @set, @scope = set, scope + @options = (@scope[:options] || {}).merge(options) + @path = normalize_path(path) normalize_options! end @@ -54,28 +55,6 @@ module ActionDispatch end private - def extract_path_and_options(args) - options = args.extract_options! - - if using_to_shorthand?(args, options) - path, to = options.find { |name, value| name.is_a?(String) } - options.merge!(:to => to).delete(path) if path - else - path = args.first - end - - if path.match(':controller') - raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module] - - # Add a default constraint for :controller path segments that matches namespaced - # controllers with default routes like :controller/:action/:id(.:format), e.g: - # GET /admin/products/show/1 - # => { :controller => 'admin/products', :action => 'show', :id => '1' } - options.reverse_merge!(:controller => /.+?/) - end - - [ normalize_path(path), options ] - end def normalize_options! path_without_format = @path.sub(/\(\.:format\)$/, '') @@ -83,25 +62,39 @@ module ActionDispatch if using_match_shorthand?(path_without_format, @options) to_shorthand = @options[:to].blank? @options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1') - @options[:as] ||= path_without_format[1..-1].gsub("/", "_") + @options[:as] ||= Mapper.normalize_name(path_without_format) end @options.merge!(default_controller_and_action(to_shorthand)) end - # match "account" => "account#index" - def using_to_shorthand?(args, options) - args.empty? && options.present? - end - # match "account/overview" def using_match_shorthand?(path, options) path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$} end def normalize_path(path) - raise ArgumentError, "path is required" if @scope[:path].blank? && path.blank? - Mapper.normalize_path("#{@scope[:path]}/#{path}") + raise ArgumentError, "path is required" if path.blank? + path = Mapper.normalize_path(path) + + if path.match(':controller') + raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module] + + # Add a default constraint for :controller path segments that matches namespaced + # controllers with default routes like :controller/:action/:id(.:format), e.g: + # GET /admin/products/show/1 + # => { :controller => 'admin/products', :action => 'show', :id => '1' } + @options.reverse_merge!(:controller => /.+?/) + end + + if @options[:format] == false + @options.delete(:format) + path + elsif path.include?(":format") + path + else + "#{path}(.:format)" + end end def app @@ -224,6 +217,10 @@ module ActionDispatch path end + def self.normalize_name(name) + normalize_path(name)[1..-1].gsub("/", "_") + end + module Base def initialize(set) #:nodoc: @set = set @@ -233,8 +230,8 @@ module ActionDispatch match '/', options.reverse_merge(:as => :root) end - def match(*args) - mapping = Mapping.new(@set, @scope, args).to_route + def match(path, options=nil) + mapping = Mapping.new(@set, @scope, path, options || {}).to_route @set.add_route(*mapping) self end @@ -250,7 +247,7 @@ module ActionDispatch raise "A rack application must be specified" unless path - match(path, options.merge(:to => app, :anchor => false)) + match(path, options.merge(:to => app, :anchor => false, :format => false)) self end @@ -332,13 +329,7 @@ module ActionDispatch ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller end - case args.first - when String - options[:path] = args.first - when Symbol - options[:controller] = args.first - end - + options[:path] = args.first if args.first.is_a?(String) recover = {} options[:constraints] ||= {} @@ -370,8 +361,9 @@ module ActionDispatch @scope[:blocks] = recover[:block] end - def controller(controller) - scope(controller.to_sym) { yield } + def controller(controller, options={}) + options[:controller] = controller + scope(options) { yield } end def namespace(path, options = {}) @@ -389,21 +381,6 @@ module ActionDispatch scope(:defaults => defaults) { yield } end - def match(*args) - options = args.extract_options! - - options = (@scope[:options] || {}).merge(options) - - if @scope[:as] && !options[:as].blank? - options[:as] = "#{@scope[:as]}_#{options[:as]}" - elsif @scope[:as] && options[:as] == "" - options[:as] = @scope[:as].to_s - end - - args.push(options) - super(*args) - end - private def scope_options @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym } @@ -467,9 +444,9 @@ module ActionDispatch module Resources # CANONICAL_ACTIONS holds all actions that does not need a prefix or # a path appended since they fit properly in their scope level. - VALID_ON_OPTIONS = [:new, :collection, :member] - CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy] - RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except] + VALID_ON_OPTIONS = [:new, :collection, :member] + RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except] + CANONICAL_ACTIONS = %w(index create new show update destroy) class Resource #:nodoc: DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit] @@ -723,42 +700,27 @@ module ActionDispatch return member { match(*args) } end - path = options.delete(:path) - action = args.first - - if action.is_a?(Symbol) - path = path_for_action(action, path) - options[:to] ||= action - options[:as] = name_for_action(action, options[:as]) - - with_exclusive_scope do - return super(path, options) - end - elsif resource_method_scope? - path = path_for_custom_action - options[:action] ||= action - options[:as] = name_for_action(options[:as]) if options[:as] - args.push(options) - - with_exclusive_scope do - scope(path) do - return super - end - end - end - if resource_scope? raise ArgumentError, "can't define route directly in resource(s) scope" end - args.push(options) - super + action = args.first + path = path_for_action(action, options.delete(:path)) + + if action.to_s =~ /^[\w\/]+$/ + options[:action] ||= action unless action.to_s.include?("/") + options[:as] = name_for_action(action, options[:as]) + else + options[:as] = name_for_action(options[:as]) + end + + super(path, options) end def root(options={}) if @scope[:scope_level] == :resources - with_scope_level(:nested) do - scope(parent_resource.path, :as => parent_resource.collection_name) do + with_scope_level(:root) do + scope(parent_resource.path) do super(options) end end @@ -895,7 +857,7 @@ module ActionDispatch end def canonical_action?(action, flag) - flag && CANONICAL_ACTIONS.include?(action) + flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s) end def shallow_scoping? @@ -906,18 +868,10 @@ module ActionDispatch prefix = shallow_scoping? ? "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path] - if canonical_action?(action, path.blank?) - "#{prefix}(.:format)" - else - "#{prefix}/#{action_path(action, path)}(.:format)" - end - end - - def path_for_custom_action - if shallow_scoping? - "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" + path = if canonical_action?(action, path.blank?) + prefix.to_s else - @scope[:path] + "#{prefix}/#{action_path(action, path)}" end end @@ -927,44 +881,59 @@ module ActionDispatch def prefix_name_for_action(action, as) if as.present? - "#{as}_" + as.to_s elsif as - "" + nil elsif !canonical_action?(action, @scope[:scope_level]) - "#{action}_" + action.to_s end end def name_for_action(action, as=nil) prefix = prefix_name_for_action(action, as) + prefix = Mapper.normalize_name(prefix) if prefix name_prefix = @scope[:as] if parent_resource collection_name = parent_resource.collection_name member_name = parent_resource.member_name - name_prefix = "#{name_prefix}_" if name_prefix.present? end - case @scope[:scope_level] + name = case @scope[:scope_level] when :collection - "#{prefix}#{name_prefix}#{collection_name}" + [prefix, name_prefix, collection_name] when :new - "#{prefix}new_#{name_prefix}#{member_name}" + [prefix, :new, name_prefix, member_name] + when :member + [prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name] + when :root + [name_prefix, collection_name, prefix] else - if shallow_scoping? - shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present? - "#{prefix}#{shallow_prefix}#{member_name}" - else - "#{prefix}#{name_prefix}#{member_name}" - end + [name_prefix, member_name, prefix] end + + name.select(&:present?).join("_").presence end end + module Shorthand + def match(*args) + if args.size == 1 && args.last.is_a?(Hash) + options = args.pop + path, to = options.find { |name, value| name.is_a?(String) } + options.merge!(:to => to).delete(path) + super(path, options) + else + super + end + end + end + include Base include HttpHelpers include Scoping include Resources + include Shorthand end end end diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 7eaf7d0534..73e16daa29 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,9 +1,9 @@ module ActionPack module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index e8751747d1..32a0f40614 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -208,7 +208,7 @@ class LayoutStatusIsRenderedTest < ActionController::TestCase end end -unless Config::CONFIG['host_os'] =~ /mswin|mingw/ +unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ class LayoutSymlinkedTest < LayoutTest layout "symlinked/symlinked_layout" end diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index de2cab3e79..e959b41219 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -549,6 +549,14 @@ XML assert_blank @request.params[:foo] end + def test_symbolized_path_params_reset_after_request + get :test_params, :id => "foo" + assert_equal "foo", @request.symbolized_path_parameters[:id] + @request.recycle! + get :test_params + assert_nil @request.symbolized_path_parameters[:id] + end + def test_should_have_knowledge_of_client_side_cookie_state_even_if_they_are_not_set @request.cookies['foo'] = 'bar' get :no_op diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index c529db4771..5b2547e700 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -40,6 +40,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get :new, :path => "build" post :create, :path => "create", :as => "" put :update + get :remove, :action => :destroy, :as => :remove + end + + scope "pagemark", :controller => "pagemarks", :as => :pagemark do + get "new", :path => "build" + post "create", :as => "" + put "update" get "remove", :action => :destroy, :as => :remove end @@ -188,7 +195,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end - resources :sheep + resources :sheep do + get "_it", :on => :member + end resources :clients do namespace :google do @@ -201,22 +210,27 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end resources :customers do - get "recent" => "customers#recent", :as => :recent, :on => :collection - get "profile" => "customers#profile", :as => :profile, :on => :member - post "preview" => "customers#preview", :as => :preview, :on => :new + get :recent, :on => :collection + get "profile", :on => :member + get "secret/profile" => "customers#secret", :on => :member + post "preview" => "customers#preview", :as => :another_preview, :on => :new resource :avatar do - get "thumbnail(.:format)" => "avatars#thumbnail", :as => :thumbnail, :on => :member + get "thumbnail" => "avatars#thumbnail", :as => :thumbnail, :on => :member end resources :invoices do - get "outstanding" => "invoices#outstanding", :as => :outstanding, :on => :collection + get "outstanding" => "invoices#outstanding", :on => :collection get "overdue", :to => :overdue, :on => :collection get "print" => "invoices#print", :as => :print, :on => :member post "preview" => "invoices#preview", :as => :preview, :on => :new + get "aged/:months", :on => :collection, :action => :aged, :as => :aged end resources :notes, :shallow => true do get "preview" => "notes#preview", :as => :preview, :on => :new get "print" => "notes#print", :as => :print, :on => :member end + get "inactive", :on => :collection + post "deactivate", :on => :member + get "old", :on => :collection, :as => :stale end namespace :api do @@ -642,15 +656,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest with_test_routes do get '/bookmark/build' assert_equal 'bookmarks#new', @response.body - assert_equal '/bookmark/build', new_bookmark_path + assert_equal '/bookmark/build', bookmark_new_path post '/bookmark/create' assert_equal 'bookmarks#create', @response.body assert_equal '/bookmark/create', bookmark_path - put '/bookmark' + put '/bookmark/update' assert_equal 'bookmarks#update', @response.body - assert_equal '/bookmark', update_bookmark_path + assert_equal '/bookmark/update', bookmark_update_path get '/bookmark/remove' assert_equal 'bookmarks#destroy', @response.body @@ -658,6 +672,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_pagemarks + with_test_routes do + get '/pagemark/build' + assert_equal 'pagemarks#new', @response.body + assert_equal '/pagemark/build', pagemark_new_path + + post '/pagemark/create' + assert_equal 'pagemarks#create', @response.body + assert_equal '/pagemark/create', pagemark_path + + put '/pagemark/update' + assert_equal 'pagemarks#update', @response.body + assert_equal '/pagemark/update', pagemark_update_path + + get '/pagemark/remove' + assert_equal 'pagemarks#destroy', @response.body + assert_equal '/pagemark/remove', pagemark_remove_path + end + end + def test_admin with_test_routes do get '/admin', {}, {'REMOTE_ADDR' => '192.168.1.100'} @@ -992,6 +1026,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal '/sheep/1', sheep_path(1) assert_equal '/sheep/new', new_sheep_path assert_equal '/sheep/1/edit', edit_sheep_path(1) + assert_equal '/sheep/1/_it', _it_sheep_path(1) end end @@ -1557,7 +1592,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest with_test_routes do assert_equal '/customers/recent', recent_customers_path assert_equal '/customers/1/profile', profile_customer_path(:id => '1') - assert_equal '/customers/new/preview', preview_new_customer_path + assert_equal '/customers/1/secret/profile', secret_profile_customer_path(:id => '1') + assert_equal '/customers/new/preview', another_preview_new_customer_path assert_equal '/customers/1/avatar/thumbnail.jpg', thumbnail_customer_avatar_path(:customer_id => '1', :format => :jpg) assert_equal '/customers/1/invoices/outstanding', outstanding_customer_invoices_path(:customer_id => '1') assert_equal '/customers/1/invoices/2/print', print_customer_invoice_path(:customer_id => '1', :id => '2') @@ -1570,6 +1606,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/customers/1/invoices/overdue' assert_equal 'invoices#overdue', @response.body + + get '/customers/1/secret/profile' + assert_equal 'customers#secret', @response.body end end @@ -2034,6 +2073,24 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest verify_redirect 'http://www.example.com/countries/all/cities' end + def test_custom_resource_actions_defined_using_string + get '/customers/inactive' + assert_equal 'customers#inactive', @response.body + assert_equal '/customers/inactive', inactive_customers_path + + post '/customers/1/deactivate' + assert_equal 'customers#deactivate', @response.body + assert_equal '/customers/1/deactivate', deactivate_customer_path(:id => '1') + + get '/customers/old' + assert_equal 'customers#old', @response.body + assert_equal '/customers/old', stale_customers_path + + get '/customers/1/invoices/aged/3' + assert_equal 'invoices#aged', @response.body + assert_equal '/customers/1/invoices/aged/3', aged_customer_invoices_path(:customer_id => '1', :months => '3') + end + private def with_test_routes yield diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb index f2f4b15520..ece4431225 100644 --- a/activemodel/lib/active_model/version.rb +++ b/activemodel/lib/active_model/version.rb @@ -1,9 +1,9 @@ module ActiveModel module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index a3c0acb370..dd9dc288cd 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -23,6 +23,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('arel', '~> 0.4.0') + s.add_dependency('arel', '~> 1.0.0.rc1') s.add_dependency('tzinfo', '~> 0.3.23') end diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 5ac89a93c2..63224377a3 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -215,7 +215,7 @@ module ActiveRecord source = reflection.source_reflection.name through_records.first.class.preload_associations(through_records, source) if through_reflection.macro == :belongs_to - rev_id_to_record_map, rev_ids = construct_id_map(records, through_primary_key) + rev_id_to_record_map = construct_id_map(records, through_primary_key).first rev_primary_key = through_reflection.klass.primary_key through_records.each do |through_record| add_preloaded_record_to_collection(rev_id_to_record_map[through_record[rev_primary_key].to_s], @@ -243,7 +243,6 @@ module ActiveRecord if options[:through] through_records = preload_through_records(records, reflection, options[:through]) - through_reflection = reflections[options[:through]] unless through_records.empty? source = reflection.source_reflection.name through_records.first.class.preload_associations(through_records, source, options) @@ -261,7 +260,6 @@ module ActiveRecord def preload_through_records(records, reflection, through_association) through_reflection = reflections[through_association] - through_primary_key = through_reflection.primary_key_name through_records = [] if reflection.options[:source_type] @@ -372,7 +370,7 @@ module ActiveRecord conditions << append_conditions(reflection, preload_options) find_options = { - :select => preload_options[:select] || options[:select] || "#{table_name}.*", + :select => preload_options[:select] || options[:select] || Arel::SqlLiteral.new("#{table_name}.*"), :include => preload_options[:include] || options[:include], :conditions => [conditions, ids], :joins => options[:joins], diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 862a19587d..e983f86f9e 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -24,7 +24,7 @@ module ActiveRecord protected def construct_find_options!(options) - options[:joins] = @join_sql + options[:joins] = Arel::SqlLiteral.new @join_sql options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select]) options[:select] ||= (@reflection.options[:select] || '*') end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index bbb44f0e17..4bbf5cd3d1 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -32,7 +32,7 @@ module ActiveRecord #:nodoc: # Active Record objects. The mapping that binds a given Active Record class to a certain # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones. # - # See the mapping rules in table_name and the full example in link:files/README.html for more insight. + # See the mapping rules in table_name and the full example in link:files/activerecord/README_rdoc.html for more insight. # # == Creation # diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb deleted file mode 100644 index b5bf7f46ef..0000000000 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ /dev/null @@ -1,644 +0,0 @@ -# encoding: utf-8 - -require 'mysql2' unless defined? Mysql2 - -module ActiveRecord - class Base - def self.mysql2_connection(config) - config[:username] = 'root' if config[:username].nil? - - if Mysql2::Client.const_defined? :FOUND_ROWS - config[:flags] = Mysql2::Client::FOUND_ROWS - end - - client = Mysql2::Client.new(config.symbolize_keys) - options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0] - ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config) - end - end - - module ConnectionAdapters - class Mysql2Column < Column - BOOL = "tinyint(1)" - def extract_default(default) - if sql_type =~ /blob/i || type == :text - if default.blank? - return null ? nil : '' - else - raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}" - end - elsif missing_default_forged_as_empty_string?(default) - nil - else - super - end - end - - def has_default? - return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns - super - end - - # Returns the Ruby class that corresponds to the abstract data type. - def klass - case type - when :integer then Fixnum - when :float then Float - when :decimal then BigDecimal - when :datetime then Time - when :date then Date - when :timestamp then Time - when :time then Time - when :text, :string then String - when :binary then String - when :boolean then Object - end - end - - def type_cast(value) - return nil if value.nil? - case type - when :string then value - when :text then value - when :integer then value.to_i rescue value ? 1 : 0 - when :float then value.to_f # returns self if it's already a Float - when :decimal then self.class.value_to_decimal(value) - when :datetime, :timestamp then value.class == Time ? value : self.class.string_to_time(value) - when :time then value.class == Time ? value : self.class.string_to_dummy_time(value) - when :date then value.class == Date ? value : self.class.string_to_date(value) - when :binary then value - when :boolean then self.class.value_to_boolean(value) - else value - end - end - - def type_cast_code(var_name) - case type - when :string then nil - when :text then nil - when :integer then "#{var_name}.to_i rescue #{var_name} ? 1 : 0" - when :float then "#{var_name}.to_f" - when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})" - when :datetime, :timestamp then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_time(#{var_name})" - when :time then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_dummy_time(#{var_name})" - when :date then "#{var_name}.class == Date ? #{var_name} : #{self.class.name}.string_to_date(#{var_name})" - when :binary then nil - when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})" - else nil - end - end - - private - def simplified_type(field_type) - return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL) - return :string if field_type =~ /enum/i or field_type =~ /set/i - return :integer if field_type =~ /year/i - return :binary if field_type =~ /bit/i - super - end - - def extract_limit(sql_type) - case sql_type - when /blob|text/i - case sql_type - when /tiny/i - 255 - when /medium/i - 16777215 - when /long/i - 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases - else - super # we could return 65535 here, but we leave it undecorated by default - end - when /^bigint/i; 8 - when /^int/i; 4 - when /^mediumint/i; 3 - when /^smallint/i; 2 - when /^tinyint/i; 1 - else - super - end - end - - # MySQL misreports NOT NULL column default when none is given. - # We can't detect this for columns which may have a legitimate '' - # default (string) but we can for others (integer, datetime, boolean, - # and the rest). - # - # Test whether the column has default '', is not null, and is not - # a type allowing default ''. - def missing_default_forged_as_empty_string?(default) - type != :string && !null && default == '' - end - end - - class Mysql2Adapter < AbstractAdapter - cattr_accessor :emulate_booleans - self.emulate_booleans = true - - ADAPTER_NAME = 'Mysql2' - PRIMARY = "PRIMARY" - - LOST_CONNECTION_ERROR_MESSAGES = [ - "Server shutdown in progress", - "Broken pipe", - "Lost connection to MySQL server during query", - "MySQL server has gone away" ] - - QUOTED_TRUE, QUOTED_FALSE = '1', '0' - - NATIVE_DATABASE_TYPES = { - :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", - :string => { :name => "varchar", :limit => 255 }, - :text => { :name => "text" }, - :integer => { :name => "int", :limit => 4 }, - :float => { :name => "float" }, - :decimal => { :name => "decimal" }, - :datetime => { :name => "datetime" }, - :timestamp => { :name => "datetime" }, - :time => { :name => "time" }, - :date => { :name => "date" }, - :binary => { :name => "blob" }, - :boolean => { :name => "tinyint", :limit => 1 } - } - - def initialize(connection, logger, connection_options, config) - super(connection, logger) - @connection_options, @config = connection_options, config - @quoted_column_names, @quoted_table_names = {}, {} - configure_connection - end - - def adapter_name - ADAPTER_NAME - end - - def supports_migrations? - true - end - - def supports_primary_key? - true - end - - def supports_savepoints? - true - end - - def native_database_types - NATIVE_DATABASE_TYPES - end - - # QUOTING ================================================== - - def quote(value, column = nil) - if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary) - s = column.class.string_to_binary(value).unpack("H*")[0] - "x'#{s}'" - elsif value.kind_of?(BigDecimal) - value.to_s("F") - else - super - end - end - - def quote_column_name(name) #:nodoc: - @quoted_column_names[name] ||= "`#{name}`" - end - - def quote_table_name(name) #:nodoc: - @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`') - end - - def quote_string(string) - @connection.escape(string) - end - - def quoted_true - QUOTED_TRUE - end - - def quoted_false - QUOTED_FALSE - end - - # REFERENTIAL INTEGRITY ==================================== - - def disable_referential_integrity(&block) #:nodoc: - old = select_value("SELECT @@FOREIGN_KEY_CHECKS") - - begin - update("SET FOREIGN_KEY_CHECKS = 0") - yield - ensure - update("SET FOREIGN_KEY_CHECKS = #{old}") - end - end - - # CONNECTION MANAGEMENT ==================================== - - def active? - return false unless @connection - @connection.query 'select 1' - true - rescue Mysql2::Error - false - end - - def reconnect! - disconnect! - connect - end - - # this is set to true in 2.3, but we don't want it to be - def requires_reloading? - false - end - - def disconnect! - unless @connection.nil? - @connection.close - @connection = nil - end - end - - def reset! - disconnect! - connect - end - - # DATABASE STATEMENTS ====================================== - - # FIXME: re-enable the following once a "better" query_cache solution is in core - # - # The overrides below perform much better than the originals in AbstractAdapter - # because we're able to take advantage of mysql2's lazy-loading capabilities - # - # # Returns a record hash with the column names as keys and column values - # # as values. - # def select_one(sql, name = nil) - # result = execute(sql, name) - # result.each(:as => :hash) do |r| - # return r - # end - # end - # - # # Returns a single value from a record - # def select_value(sql, name = nil) - # result = execute(sql, name) - # if first = result.first - # first.first - # end - # end - # - # # Returns an array of the values of the first column in a select: - # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] - # def select_values(sql, name = nil) - # execute(sql, name).map { |row| row.first } - # end - - # Returns an array of arrays containing the field values. - # Order is the same as that returned by +columns+. - def select_rows(sql, name = nil) - execute(sql, name).to_a - end - - # Executes the SQL statement in the context of this connection. - def execute(sql, name = nil) - # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been - # made since we established the connection - @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone - if name == :skip_logging - @connection.query(sql) - else - log(sql, name) { @connection.query(sql) } - end - rescue ActiveRecord::StatementInvalid => exception - if exception.message.split(":").first =~ /Packets out of order/ - raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings." - else - raise - end - end - - def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) - super - id_value || @connection.last_id - end - alias :create :insert_sql - - def update_sql(sql, name = nil) - super - @connection.affected_rows - end - - def begin_db_transaction - execute "BEGIN" - rescue Exception - # Transactions aren't supported - end - - def commit_db_transaction - execute "COMMIT" - rescue Exception - # Transactions aren't supported - end - - def rollback_db_transaction - execute "ROLLBACK" - rescue Exception - # Transactions aren't supported - end - - def create_savepoint - execute("SAVEPOINT #{current_savepoint_name}") - end - - def rollback_to_savepoint - execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}") - end - - def release_savepoint - execute("RELEASE SAVEPOINT #{current_savepoint_name}") - end - - def add_limit_offset!(sql, options) - limit, offset = options[:limit], options[:offset] - if limit && offset - sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}" - elsif limit - sql << " LIMIT #{sanitize_limit(limit)}" - elsif offset - sql << " OFFSET #{offset.to_i}" - end - sql - end - - # SCHEMA STATEMENTS ======================================== - - def structure_dump - if supports_views? - sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'" - else - sql = "SHOW TABLES" - end - - select_all(sql).inject("") do |structure, table| - table.delete('Table_type') - structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n" - end - end - - def recreate_database(name, options = {}) - drop_database(name) - create_database(name, options) - end - - # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>. - # Charset defaults to utf8. - # - # Example: - # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin' - # create_database 'matt_development' - # create_database 'matt_development', :charset => :big5 - def create_database(name, options = {}) - if options[:collation] - execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`" - else - execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`" - end - end - - def drop_database(name) #:nodoc: - execute "DROP DATABASE IF EXISTS `#{name}`" - end - - def current_database - select_value 'SELECT DATABASE() as db' - end - - # Returns the database character set. - def charset - show_variable 'character_set_database' - end - - # Returns the database collation strategy. - def collation - show_variable 'collation_database' - end - - def tables(name = nil) - tables = [] - execute("SHOW TABLES", name).each do |field| - tables << field.first - end - tables - end - - def drop_table(table_name, options = {}) - super(table_name, options) - end - - def indexes(table_name, name = nil) - indexes = [] - current_index = nil - result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name) - result.each(:symbolize_keys => true, :as => :hash) do |row| - if current_index != row[:Key_name] - next if row[:Key_name] == PRIMARY # skip the primary key - current_index = row[:Key_name] - indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, []) - end - - indexes.last.columns << row[:Column_name] - end - indexes - end - - def columns(table_name, name = nil) - sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" - columns = [] - result = execute(sql, :skip_logging) - result.each(:symbolize_keys => true, :as => :hash) { |field| - columns << Mysql2Column.new(field[:Field], field[:Default], field[:Type], field[:Null] == "YES") - } - columns - end - - def create_table(table_name, options = {}) - super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB")) - end - - def rename_table(table_name, new_name) - execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}" - end - - def add_column(table_name, column_name, type, options = {}) - add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - add_column_options!(add_column_sql, options) - add_column_position!(add_column_sql, options) - execute(add_column_sql) - end - - def change_column_default(table_name, column_name, default) - column = column_for(table_name, column_name) - change_column table_name, column_name, column.sql_type, :default => default - end - - def change_column_null(table_name, column_name, null, default = nil) - column = column_for(table_name, column_name) - - unless null || default.nil? - execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") - end - - change_column table_name, column_name, column.sql_type, :null => null - end - - def change_column(table_name, column_name, type, options = {}) - column = column_for(table_name, column_name) - - unless options_include_default?(options) - options[:default] = column.default - end - - unless options.has_key?(:null) - options[:null] = column.null - end - - change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - add_column_options!(change_column_sql, options) - add_column_position!(change_column_sql, options) - execute(change_column_sql) - end - - def rename_column(table_name, column_name, new_column_name) - options = {} - if column = columns(table_name).find { |c| c.name == column_name.to_s } - options[:default] = column.default - options[:null] = column.null - else - raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" - end - current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"] - rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}" - add_column_options!(rename_column_sql, options) - execute(rename_column_sql) - end - - # Maps logical Rails types to MySQL-specific data types. - def type_to_sql(type, limit = nil, precision = nil, scale = nil) - return super unless type.to_s == 'integer' - - case limit - when 1; 'tinyint' - when 2; 'smallint' - when 3; 'mediumint' - when nil, 4, 11; 'int(11)' # compatibility with MySQL default - when 5..8; 'bigint' - else raise(ActiveRecordError, "No integer type has byte size #{limit}") - end - end - - def add_column_position!(sql, options) - if options[:first] - sql << " FIRST" - elsif options[:after] - sql << " AFTER #{quote_column_name(options[:after])}" - end - end - - def show_variable(name) - variables = select_all("SHOW VARIABLES LIKE '#{name}'") - variables.first['Value'] unless variables.empty? - end - - def pk_and_sequence_for(table) - keys = [] - result = execute("describe #{quote_table_name(table)}") - result.each(:symbolize_keys => true, :as => :hash) do |row| - keys << row[:Field] if row[:Key] == "PRI" - end - keys.length == 1 ? [keys.first, nil] : nil - end - - # Returns just a table's primary key - def primary_key(table) - pk_and_sequence = pk_and_sequence_for(table) - pk_and_sequence && pk_and_sequence.first - end - - def case_sensitive_equality_operator - "= BINARY" - end - - def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) - where_sql - end - - protected - def quoted_columns_for_index(column_names, options = {}) - length = options[:length] if options.is_a?(Hash) - - quoted_column_names = case length - when Hash - column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) } - when Fixnum - column_names.map {|name| "#{quote_column_name(name)}(#{length})"} - else - column_names.map {|name| quote_column_name(name) } - end - end - - def translate_exception(exception, message) - return super unless exception.respond_to?(:error_number) - - case exception.error_number - when 1062 - RecordNotUnique.new(message, exception) - when 1452 - InvalidForeignKey.new(message, exception) - else - super - end - end - - private - def connect - @connection = Mysql2::Client.new(@config) - configure_connection - end - - def configure_connection - @connection.query_options.merge!(:as => :array) - encoding = @config[:encoding] - execute("SET NAMES '#{encoding}'", :skip_logging) if encoding - - # By default, MySQL 'where id is null' selects the last inserted id. - # Turn this off. http://dev.rubyonrails.org/ticket/6778 - execute("SET SQL_AUTO_IS_NULL=0", :skip_logging) - end - - # Returns an array of record hashes with the column names as keys and - # column values as values. - def select(sql, name = nil) - execute(sql, name).each(:as => :hash) - end - - def supports_views? - version[0] >= 5 - end - - def version - @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } - end - - def column_for(table_name, column_name) - unless column = columns(table_name).find { |c| c.name == column_name.to_s } - raise "No such column: #{table_name}.#{column_name}" - end - column - end - end - end -end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index c342824712..1ce86da2fa 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -218,18 +218,20 @@ module ActiveRecord # @brake.touch #=> will also invoke @brake.car.touch and @brake.car.corporation.touch def touch(name = nil) attributes = timestamp_attributes_for_update_in_model - attributes << name if name + unless attributes.blank? + attributes << name if name - current_time = current_time_from_proper_timezone - changes = {} + current_time = current_time_from_proper_timezone + changes = {} - attributes.each do |column| - changes[column.to_s] = write_attribute(column.to_s, current_time) - end + attributes.each do |column| + changes[column.to_s] = write_attribute(column.to_s, current_time) + end - @changed_attributes.except!(*changes.keys) - primary_key = self.class.primary_key - self.class.update_all(changes, { primary_key => self[primary_key] }) == 1 + @changed_attributes.except!(*changes.keys) + primary_key = self.class.primary_key + self.class.update_all(changes, { primary_key => self[primary_key] }) == 1 + end end private diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 03b06205d4..b7ed81c300 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -154,7 +154,7 @@ module ActiveRecord else # Apply limit and order only if they're both present if @limit_value.present? == @order_values.present? - arel.update(@klass.send(:sanitize_sql_for_assignment, updates)) + arel.update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates))) else except(:limit, :order).update_all(updates) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0b5e9b4fb2..8ccc62c9d1 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -121,8 +121,10 @@ module ActiveRecord when Hash, Array, Symbol if array_of_strings?(join) join_string = join.join(' ') - arel = arel.join(join_string) + arel = arel.join(Arel::SqlLiteral.new(join_string)) end + when String + arel = arel.join(Arel::SqlLiteral.new(join)) else arel = arel.join(join) end diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index c6ff4b39fa..a7583f06cc 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -61,7 +61,7 @@ module ActiveRecord end def should_record_timestamps? - record_timestamps && !partial_updates? || changed? + record_timestamps && (!partial_updates? || changed?) end def timestamp_attributes_for_update_in_model diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index a467ffa960..89eba15be1 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -1,9 +1,9 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 28234ebe49..742513230e 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -75,8 +75,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase end def test_eager_loading_with_primary_key - apple = Firm.create("name" => "Apple") - citibank = Client.create("name" => "Citibank", :firm_name => "Apple") + Firm.create("name" => "Apple") + Client.create("name" => "Citibank", :firm_name => "Apple") citibank_result = Client.find(:first, :conditions => {:name => "Citibank"}, :include => :firm_with_primary_key) assert_not_nil citibank_result.instance_variable_get("@firm_with_primary_key") end 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 ed7d9a782c..c726fedd13 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 @@ -109,7 +109,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase record = con.select_rows(sql).last assert_not_nil record[2] assert_not_nil record[3] - if current_adapter?(:Mysql2Adapter) + if current_adapter?(:Mysql2Adapter, :OracleAdapter) assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2].to_s(:db) assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3].to_s(:db) else diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index a698f23fb6..9b3cc47c79 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -120,9 +120,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal developer.created_at_before_type_cast, "345643456" assert_equal developer.created_at, nil - developer.created_at = "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") + developer.created_at = "2010-03-21 21:23:32" + assert_equal developer.created_at_before_type_cast, "2010-03-21 21:23:32" + assert_equal developer.created_at, Time.parse("2010-03-21 21:23:32") end end end diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index fce27d8972..db7accfc40 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -3,13 +3,15 @@ require 'models/developer' require 'models/owner' require 'models/pet' require 'models/toy' +require 'models/car' class TimestampTest < ActiveRecord::TestCase - fixtures :developers, :owners, :pets, :toys + fixtures :developers, :owners, :pets, :toys, :cars def setup @developer = Developer.first @previously_updated_at = @developer.updated_at + @car = Car.first end def test_saving_a_changed_record_updates_its_timestamp @@ -38,6 +40,16 @@ class TimestampTest < ActiveRecord::TestCase assert_equal previous_salary, @developer.salary end + def test_saving_when_record_timestamps_is_false_doesnt_update_its_timestamp + Developer.record_timestamps = false + @developer.name = "John Smith" + @developer.save! + + assert_equal @previously_updated_at, @developer.updated_at + ensure + Developer.record_timestamps = true + end + def test_touching_a_different_attribute previously_created_at = @developer.created_at @developer.touch(:created_at) @@ -48,6 +60,10 @@ class TimestampTest < ActiveRecord::TestCase assert_not_equal @previously_updated_at, @developer.updated_at end + def test_touch_a_record_without_timestamps + assert_nothing_raised { @car.touch } + end + def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at pet = Pet.first owner = pet.owner diff --git a/activerecord/test/connections/native_oracle/connection.rb b/activerecord/test/connections/native_oracle/connection.rb index 00164466f2..9a717018b2 100644 --- a/activerecord/test/connections/native_oracle/connection.rb +++ b/activerecord/test/connections/native_oracle/connection.rb @@ -8,32 +8,22 @@ print "Using Oracle\n" require_dependency 'models/course' require 'logger' -# ActiveRecord::Base.logger = Logger.new STDOUT -# ActiveRecord::Base.logger.level = Logger::WARN ActiveRecord::Base.logger = Logger.new("debug.log") # Set these to your database connection strings -db = ENV['ARUNIT_DB_NAME'] = 'orcl' +ENV['ARUNIT_DB_NAME'] ||= 'orcl' ActiveRecord::Base.configurations = { 'arunit' => { :adapter => 'oracle_enhanced', - :database => db, - :host => "localhost", # used just by JRuby to construct JDBC connect string - # :adapter => "jdbc", - # :driver => "oracle.jdbc.driver.OracleDriver", - # :url => "jdbc:oracle:thin:@localhost:1521:#{db}", + :database => ENV['ARUNIT_DB_NAME'], :username => 'arunit', :password => 'arunit', :emulate_oracle_adapter => true }, 'arunit2' => { :adapter => 'oracle_enhanced', - :database => db, - :host => "localhost", # used just by JRuby to construct JDBC connect string - # :adapter => "jdbc", - # :driver => "oracle.jdbc.driver.OracleDriver", - # :url => "jdbc:oracle:thin:@localhost:1521:#{db}", + :database => ENV['ARUNIT_DB_NAME'], :username => 'arunit2', :password => 'arunit2', :emulate_oracle_adapter => true @@ -43,9 +33,6 @@ ActiveRecord::Base.configurations = { ActiveRecord::Base.establish_connection 'arunit' Course.establish_connection 'arunit2' -# ActiveRecord::Base.connection.execute %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'} -# ActiveRecord::Base.connection.execute %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} rescue nil - # for assert_queries test helper ActiveRecord::Base.connection.class.class_eval do IGNORED_SELECT_SQL = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from ((all|user)_tab_columns|(all|user)_triggers|(all|user)_constraints)/im] @@ -58,6 +45,3 @@ ActiveRecord::Base.connection.class.class_eval do alias_method_chain :select, :query_record end - -# For JRuby Set default $KCODE to UTF8 -$KCODE = "UTF8" if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 62420725ad..a462f70684 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -21,7 +21,7 @@ require 'active_resource/log_subscriber' module ActiveResource # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application. # - # For an outline of what Active Resource is capable of, see link:files/vendor/rails/activeresource/README.html. + # For an outline of what Active Resource is capable of, see its {README}[link:files/activeresource/README_rdoc.html]. # # == Automated mapping # diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 43c00e9cf1..e2e0ecccbb 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -1,9 +1,9 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb index d7ebd5feef..a82cdfc360 100644 --- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb @@ -21,7 +21,8 @@ class Hash # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second. # Modifies the receiver in place. def reverse_merge!(other_hash) - merge!( other_hash ){|k,o,n| o } + # right wins if there is no left + merge!( other_hash ){|key,left,right| left } end alias_method :reverse_update, :reverse_merge! diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 7c455f66d5..5105c40e4d 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -38,7 +38,7 @@ module Kernel # puts 'But this will' def silence_stream(stream) old_stream = stream.dup - stream.reopen(Config::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') + stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') stream.sync = true yield ensure diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 019fb2df06..afbdd2c272 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -437,7 +437,7 @@ module ActiveSupport #:nodoc: begin @wrapped_string[0...byte_offset].unpack('U*').length - rescue ArgumentError => e + rescue ArgumentError byte_offset -= 1 retry end diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index d629f6f2b7..6b29ba4c10 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -34,7 +34,7 @@ module ActiveSupport module Isolation def self.forking_env? - !ENV["NO_FORK"] && ((Config::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/)) + !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/)) end def self.included(base) diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 9d2cf13260..9e8b3d7888 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -1,9 +1,9 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/rails.gemspec b/rails.gemspec index 8c90584470..c90541b9c4 100644 --- a/rails.gemspec +++ b/rails.gemspec @@ -25,5 +25,5 @@ Gem::Specification.new do |s| s.add_dependency('activeresource', version) s.add_dependency('actionmailer', version) s.add_dependency('railties', version) - s.add_dependency('bundler', '>= 1.0.0.rc.2') + s.add_dependency('bundler', '>= 1.0.0.rc.6') end diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile index fb81bab98d..91ffa8ccc3 100644 --- a/railties/guides/source/contributing_to_rails.textile +++ b/railties/guides/source/contributing_to_rails.textile @@ -69,7 +69,7 @@ All of the Rails tests must pass with any code you submit, otherwise you have no NOTE: Ensure you install bundler v1.0 <shell> -gem install -v=1.0.0.rc.2 bundler +gem install -v=1.0.0.rc.6 bundler bundle install --without db </shell> diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index c5b41673e1..b1f8ea29da 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -1,6 +1,6 @@ h2. Creating and Customizing Rails Generators -Rails generators are an essential tool if you plan to improve your workflow and in this guide you will learn how to create and customize already existing generators. +Rails generators are an essential tool if you plan to improve your workflow. With this guide you will learn how to create generators and customize existing ones. In this guide you will: @@ -13,7 +13,7 @@ In this guide you will: endprologue. -NOTE: This guide is about Rails generators for versions >= 3.0. Rails generators from previous versions are not supported. +NOTE: This guide is about generators in Rails 3, previous versions are not covered. h3. First Contact @@ -35,7 +35,7 @@ h3. Creating Your First Generator Since Rails 3.0, generators are built on top of "Thor":http://github.com/wycats/thor. Thor provides powerful options parsing and a great API for manipulating files. For instance, let's build a generator that creates an initializer file named +initializer.rb+ inside +config/initializers+. -The first step is to create a file at +RAILS_APP/lib/generators/initializer_generator.rb+ with the following content: +The first step is to create a file at +lib/generators/initializer_generator.rb+ with the following content: <ruby> class InitializerGenerator < Rails::Generators::Base @@ -74,7 +74,7 @@ Now we can see the new description by invoking +--help+ on the new generator. Th h3. Creating Generators with Generators -A faster way to create a generator is using the generator's generator: +Generators themselves have a generator: <shell> $ rails generate generator initializer @@ -84,7 +84,7 @@ $ rails generate generator initializer create lib/generators/initializer/templates </shell> -And it will create a new generator as follows: +This is the generator just created: <ruby> class InitializerGenerator < Rails::Generators::NamedBase @@ -92,7 +92,7 @@ class InitializerGenerator < Rails::Generators::NamedBase end </ruby> -First, notice that we are inheriting from +Rails::Generators::NamedBase+ instead of +Rails::Generators::Base+. This means that our generator expects as least one argument, which will be the name of the initializer. +First, notice that we are inheriting from +Rails::Generators::NamedBase+ instead of +Rails::Generators::Base+. This means that our generator expects at least one argument, which will be the name of the initializer. We can see that by invoking the description of this new generator (don't forget to delete the old generator file): @@ -102,7 +102,9 @@ Usage: rails generate initializer NAME [options] </shell> -We can also see in our new generator that it has a class method called +source_root+. This method points to where our generator templates will be placed and by default it points to the created directory under +RAILS_APP/lib/generators/initializer/templates+. In order to understand what a generator template means, let's create a file at +RAILS_APP/lib/generators/initializer/templates/initializer.rb+ with the following content: +We can also see that our new generator has a class method called +source_root+. This method points to where our generator templates will be placed, if any, and by default it points to the created directory +lib/generators/initializer/templates+. + +In order to understand what a generator template means, let's create the file +lib/generators/initializer/templates/initializer.rb+ with the following content: <ruby> # Add initialization content here @@ -124,16 +126,14 @@ end And let's execute our generator: <shell> -$ rails generate initializer foo +$ rails generate initializer core_extensions </shell> -We can see that now a initializer named foo was created at +config/initializers/foo.rb+ with the contents of our template. That means that +copy_file+ copied a file in our source root to the destination path we gave. The method +file_name+ is automatically created when we inherit from +Rails::Generators::NamedBase+. +We can see that now a initializer named core_extensions was created at +config/initializers/core_extensions.rb+ with the contents of our template. That means that +copy_file+ copied a file in our source root to the destination path we gave. The method +file_name+ is automatically created when we inherit from +Rails::Generators::NamedBase+. h3. Generators Lookup -Now that we've created our first generator, we need to briefly discuss generator lookup. The way Rails finds generators is exactly the same way Ruby find files, i.e. using +$LOAD_PATHS+. - -For instance, when you say +rails generate initializer foo+, Rails knows you want to invoke the initializer generator and then search for the following generators in the $LOAD_PATHS: +When you run +rails generate initializer core_extensions+ Rails requires these files in turn until one is found: <shell> rails/generators/initializer/initializer_generator.rb @@ -142,11 +142,13 @@ rails/generators/initializer_generator.rb generators/initializer_generator.rb </shell> -If none of them is found, it raises an error message. +If none is found you get an error message. + +INFO: The examples above put files under the application's +lib+ because said directoty belongs to +$LOAD_PATH+. h3. Customizing Your Workflow -Rails generators are flexible enough to let you customize your scaffold the way you want. In your +config/application.rb+ there is a section just for generators: +Rails own generators are flexible enough to let you customize scaffolding. They can be configured in +config/application.rb+, these are some defaults: <ruby> config.generators do |g| @@ -166,7 +168,7 @@ $ rails generate scaffold User name:string invoke test_unit create test/unit/user_test.rb create test/fixtures/users.yml - route map.resources :users + route resources :users invoke scaffold_controller create app/controllers/users_controller.rb invoke erb @@ -186,9 +188,9 @@ $ rails generate scaffold User name:string create public/stylesheets/scaffold.css </shell> -Looking at this output, it's easy to understand how generators work on Rails 3.0 and above. The scaffold generator doesn't actually generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication. +Looking at this output, it's easy to understand how generators work in Rails 3.0 and above. The scaffold generator doesn't actually generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication. -Our first customization on the workflow will be to stop generating stylesheets and test fixtures on scaffold. We can achieve that by changing our application to the following: +Our first customization on the workflow will be to stop generating stylesheets and test fixtures for scaffolds. We can achieve that by changing our configuration to the following: <ruby> config.generators do |g| @@ -199,7 +201,7 @@ config.generators do |g| end </ruby> -If we generate another resource on scaffold, we can notice that neither stylesheets nor fixtures are created anymore. If you want to customize it further, for example to use +Datamapper+ and +RSpec+ instead of +ActiveRecord+ and +TestUnit+, it's just a matter of adding their gems to your application and configuring your generators. +If we generate another resource with the scaffold generator, we can notice that neither stylesheets nor fixtures are created anymore. If you want to customize it further, for example to use DataMapper and RSpec instead of Active Record and TestUnit, it's just a matter of adding their gems to your application and configuring your generators. To demonstrate this, we are going to create a new helper generator that simply adds some instance variable readers. First, we create a generator: @@ -224,18 +226,18 @@ end We can try out our new generator by creating a helper for users: <shell> -$ rails generate my_helper users +$ rails generate my_helper products </shell> And it will generate the following helper file in +app/helpers+: <ruby> -module UsersHelper - attr_reader :users, :user +module ProductsHelper + attr_reader :products, :product end </ruby> -Which is what we expected. We can now tell scaffold to use our new helper generator by configuring +config/application.rb+ once again: +Which is what we expected. We can now tell scaffold to use our new helper generator by editing +config/application.rb+ once again: <ruby> config.generators do |g| @@ -247,7 +249,7 @@ config.generators do |g| end </ruby> -And see it in action when invoking generator once again: +and see it in action when invoking the generator: <shell> $ rails generate scaffold Post body:text @@ -260,7 +262,7 @@ We can notice on the output that our new helper was invoked instead of the Rails Since Rails 3.0, this is easy to do due to the hooks concept. Our new helper does not need to be focused in one specific test framework, it can simply provide a hook and a test framework just needs to implement this hook in order to be compatible. -To do that, we can change your generator to the following: +To do that, we can change the generator this way: <ruby> class MyHelperGenerator < Rails::Generators::NamedBase @@ -287,9 +289,9 @@ And now you can re-run scaffold for another resource and see it generating tests h3. Customizing Your Workflow by Changing Generators Templates -In the step above, we simply wanted to add a line to the generated helper, without adding any extra functionality. There is a simpler way to do that, and it's by replacing the templates of already existing generators. +In the step above we simply wanted to add a line to the generated helper, without adding any extra functionality. There is a simpler way to do that, and it's by replacing the templates of already existing generators, in that case +Rails::Generators::HelperGenerator+. -In Rails 3.0 and above, generators don't just look in the source root for templates, they also search for templates in other paths. And one of them is inside +RAILS_APP/lib/templates+. Since we want to customize +Rails::Generators::HelperGenerator+, we can do that by simply making a template copy inside +RAILS_APP/lib/templates/rails/helper+ with the name +helper.rb+. So let's create that file with the following content: +In Rails 3.0 and above, generators don't just look in the source root for templates, they also search for templates in other paths. And one of them is +lib/templates+. Since we want to customize +Rails::Generators::HelperGenerator+, we can do that by simply making a template copy inside +lib/templates/rails/helper+ with the name +helper.rb+. So let's create that file with the following content: <erb> module <%= class_name %>Helper @@ -297,7 +299,7 @@ module <%= class_name %>Helper end </erb> -So now we can revert the changes in +config/application.rb+: +and revert the last change in +config/application.rb+: <ruby> config.generators do |g| @@ -308,11 +310,11 @@ config.generators do |g| end </ruby> -If you generate another resource, you can see that we got exactly the same result! This is useful if you want to customize your scaffold templates and/or layout by just creating +edit.html.erb+, +index.html.erb+ and so on inside +RAILS_APP/lib/templates/erb/scaffold+. +If you generate another resource, you can see that we get exactly the same result! This is useful if you want to customize your scaffold templates and/or layout by just creating +edit.html.erb+, +index.html.erb+ and so on inside +lib/templates/erb/scaffold+. h3. Adding Generators Fallbacks -One last feature about generators which is quite useful for plugin generators is fallbacks. For example, imagine that you want to add a feature on top of TestUnit test framework, like "shoulda":http://github.com/thoughtbot/shoulda does. Since TestUnit already implements all generators required by Rails and shoulda just wants to overwrite part of it, there is no need for shoulda to reimplement some generators again, it can simply tell Rails to use a +TestUnit+ generator if none was found under the +Shoulda+ namespace. +One last feature about generators which is quite useful for plugin generators is fallbacks. For example, imagine that you want to add a feature on top of TestUnit like "shoulda":http://github.com/thoughtbot/shoulda does. Since TestUnit already implements all generators required by Rails and shoulda just wants to overwrite part of it, there is no need for shoulda to reimplement some generators again, it can simply tell Rails to use a +TestUnit+ generator if none was found under the +Shoulda+ namespace. We can easily simulate this behavior by changing our +config/application.rb+ once again: @@ -328,7 +330,7 @@ config.generators do |g| end </ruby> -Now, if you create a Comment scaffold, you will see that the shoulda generators are being invoked, and at the end, they are just falling back to test unit generators: +Now, if you create a Comment scaffold, you will see that the shoulda generators are being invoked, and at the end, they are just falling back to TestUnit generators: <shell> $ rails generate scaffold Comment body:text @@ -363,6 +365,8 @@ h3. Changelog "Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/102 +* August 23, 2010: Edit pass by "Xavier Noria":credits.html#fxn + * April 30, 2010: Reviewed by José Valim * November 20, 2009: First version by José Valim diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 0a2f60c30a..531ddd998b 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -150,7 +150,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This * nokogiri-1.4.3.1.gem * polyglot-0.3.1.gem * rack-1.2.1.gem -* rack-mount-0.6.10.gem +* rack-mount-0.6.12.gem * rack-test-0.5.4.gem * rails-3.0.0.beta4.gem * railties-3.0.0.beta4.gem @@ -1374,7 +1374,7 @@ the _version_ file contains this code (comments stripped): module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = "0.beta1" STRING = [MAJOR, MINOR, TINY].join('.') diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb index 5bbaf725df..14d245ab2e 100644 --- a/railties/lib/rails/commands/dbconsole.rb +++ b/railties/lib/rails/commands/dbconsole.rb @@ -42,7 +42,7 @@ module Rails def find_cmd(*commands) dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) - commands += commands.map{|cmd| "#{cmd}.exe"} if Config::CONFIG['host_os'] =~ /mswin|mingw/ + commands += commands.map{|cmd| "#{cmd}.exe"} if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ full_path_command = nil found = commands.detect do |cmd| diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb index c43a233bc3..54a9e6ec59 100644 --- a/railties/lib/rails/commands/runner.rb +++ b/railties/lib/rails/commands/runner.rb @@ -19,7 +19,7 @@ ARGV.clone.options do |opts| opts.on("-h", "--help", "Show this help message.") { $stderr.puts opts; exit } - if Config::CONFIG['host_os'] !~ /mswin|mingw/ + if RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ opts.separator "" opts.separator "You can also use runner as a shebang line for your scripts like this:" opts.separator "-------------------------------------------------------------" diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index ee3e3ba040..555bc9dbc8 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -119,7 +119,7 @@ module Rails root = File.exist?("#{root_path}/#{flag}") ? root_path : default raise "Could not find root path for #{self}" unless root - Config::CONFIG['host_os'] =~ /mswin|mingw/ ? + RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? Pathname.new(root).expand_path : Pathname.new(root).realpath end end diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 668ef48892..da64560d83 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -242,7 +242,7 @@ module Rails def rake(command, options={}) log :rake, command env = options[:env] || 'development' - sudo = options[:sudo] && Config::CONFIG['host_os'] !~ /mswin|mingw/ ? 'sudo ' : '' + sudo = options[:sudo] && RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ ? 'sudo ' : '' in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", :verbose => false) } end @@ -309,7 +309,7 @@ module Rails # Add an extension to the given name based on the platform. # def extify(name) - if Config::CONFIG['host_os'] =~ /mswin|mingw/ + if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ "#{name}.bat" else name diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index a90f109844..6eba0f77e7 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -394,6 +394,7 @@ module Rails when "postgresql" then "pg" when "sqlite3" then "sqlite3-ruby" when "frontbase" then "ruby-frontbase" + when "mysql" then "mysql2" else options[:database] end end @@ -415,7 +416,7 @@ module Rails "/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 Config::CONFIG['host_os'] =~ /mswin|mingw/ + ].find { |f| File.exist?(f) } unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end def empty_directory_with_gitkeep(destination, config = {}) diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml index ffc8a0a8cb..5d28c7c312 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml @@ -1,21 +1,12 @@ # MySQL. Versions 4.1 and 5.0 are recommended. # # Install the MySQL driver: -# gem install mysql -# On Mac OS X: -# sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql -# On Mac OS X Leopard: -# sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config -# This sets the ARCHFLAGS environment variable to your native architecture -# On Windows: -# gem install mysql -# Choose the win32 build. -# Install MySQL and put its /bin directory on your path. +# gem install mysql2 # # And be sure to use new-style password hashing: # http://dev.mysql.com/doc/refman/5.0/en/old-client.html development: - adapter: mysql + adapter: mysql2 encoding: utf8 reconnect: false database: <%= app_name %>_development @@ -32,7 +23,7 @@ development: # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: - adapter: mysql + adapter: mysql2 encoding: utf8 reconnect: false database: <%= app_name %>_test @@ -46,7 +37,7 @@ test: <% end -%> production: - adapter: mysql + adapter: mysql2 encoding: utf8 reconnect: false database: <%= app_name %>_production diff --git a/railties/lib/rails/generators/rails/generator/USAGE b/railties/lib/rails/generators/rails/generator/USAGE index d68539e4a6..d28eb3d7d8 100644 --- a/railties/lib/rails/generators/rails/generator/USAGE +++ b/railties/lib/rails/generators/rails/generator/USAGE @@ -1,6 +1,6 @@ Description: - Stubs out a new generator at lib/generators. Pass the generator name, either - CamelCased or under_scored, as an argument. + Stubs out a new generator at lib/generators. Pass the generator name as an argument, + either CamelCased or snake_cased. Example: `rails generate generator Awesome` @@ -8,4 +8,5 @@ Example: creates a standard awesome generator: lib/generators/awesome/ lib/generators/awesome/awesome_generator.rb + lib/generators/awesome/USAGE lib/generators/awesome/templates/ diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake index c0a2fe38bd..65cf79a0a2 100644 --- a/railties/lib/rails/tasks/routes.rake +++ b/railties/lib/rails/tasks/routes.rake @@ -17,7 +17,7 @@ task :routes => :environment do name = named_routes.send(key, route).to_s reqs = route.requirements.dup - reqs[:to] = route.app unless route.app.is_a?(ActionDispatch::Routing::RouteSet::Dispatcher) + reqs[:to] = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/ reqs = reqs.empty? ? "" : reqs.inspect {:name => name, :verb => route.verb.to_s, :path => route.path, :reqs => reqs} diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index 38c14fcd6b..713833f884 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -1,3 +1,4 @@ +require 'rbconfig' require 'rake/testtask' # Monkey-patch to silence the description from Rake::TestTask to cut down on rake -T noise @@ -62,7 +63,7 @@ end module Kernel def silence_stderr old_stderr = STDERR.dup - STDERR.reopen(Config::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') + STDERR.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') STDERR.sync = true yield ensure diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index c5d1d02bc1..0213d46254 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -1,9 +1,9 @@ module Rails module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/railties/railties.gemspec b/railties/railties.gemspec index d76b74190b..adb2a7610b 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.rdoc_options << '--exclude' << '.' s.has_rdoc = false - s.add_dependency('rake', '>= 0.8.3') + s.add_dependency('rake', '>= 0.8.4') s.add_dependency('thor', '~> 0.14.0') s.add_dependency('activesupport', version) s.add_dependency('actionpack', version) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 849c8001f4..dcd7629505 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -144,7 +144,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_config_another_database run_generator([destination_root, "-d", "mysql"]) assert_file "config/database.yml", /mysql/ - assert_file "Gemfile", /^gem\s+["']mysql["']$/ + assert_file "Gemfile", /^gem\s+["']mysql2["']$/ end def test_config_database_is_not_added_if_skip_active_record_is_given diff --git a/version.rb b/version.rb index c5d1d02bc1..0213d46254 100644 --- a/version.rb +++ b/version.rb @@ -1,9 +1,9 @@ module Rails module VERSION #:nodoc: MAJOR = 3 - MINOR = 0 + MINOR = 1 TINY = 0 - BUILD = "rc" + BUILD = "beta" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end |