Permalink
Browse files

Merge commit 'rails/master'

Conflicts:
	railties/guides/source/index.html.erb
  • Loading branch information...
2 parents 81807e0 + 6c280f3 commit efba1d4227514a6ce4880910a6531b0a6c3c75aa @fxn fxn committed May 1, 2010
Showing with 2,027 additions and 865 deletions.
  1. 0 {railties/lib/rails/generators/rails → actionmailer/lib/rails/generators}/mailer/USAGE
  2. +2 −0 {railties/lib/rails/generators/rails → actionmailer/lib/rails/generators}/mailer/mailer_generator.rb
  3. 0 {railties/lib/rails/generators/rails → actionmailer/lib/rails/generators}/mailer/templates/mailer.rb
  4. +6 −0 actionpack/CHANGELOG
  5. +1 −0 actionpack/lib/action_controller/base.rb
  6. +1 −1 actionpack/lib/action_controller/caching/actions.rb
  7. +158 −0 actionpack/lib/action_controller/metal/http_authentication.rb
  8. +11 −1 actionpack/lib/action_dispatch/middleware/show_exceptions.rb
  9. +0 −5 actionpack/lib/action_dispatch/routing/route_set.rb
  10. +33 −44 actionpack/lib/action_view/helpers/date_helper.rb
  11. +1 −3 actionpack/lib/action_view/helpers/url_helper.rb
  12. +31 −0 actionpack/test/controller/caching_test.rb
  13. +113 −0 actionpack/test/controller/http_token_authentication_test.rb
  14. +4 −2 actionpack/test/controller/rescue_test.rb
  15. +20 −10 actionpack/test/dispatch/routing_test.rb
  16. +80 −21 actionpack/test/template/date_helper_test.rb
  17. +6 −0 actionpack/test/template/form_tag_helper_test.rb
  18. +14 −0 actionpack/test/template/url_helper_test.rb
  19. +51 −111 activemodel/lib/active_model/serializers/xml.rb
  20. +3 −0 activemodel/lib/active_model/validations.rb
  21. +4 −2 activemodel/lib/active_model/validations/acceptance.rb
  22. +13 −2 activemodel/test/cases/serializeration/xml_serialization_test.rb
  23. +8 −0 activemodel/test/models/contact.rb
  24. +4 −0 activerecord/CHANGELOG
  25. +60 −17 activerecord/lib/active_record/associations.rb
  26. +4 −0 activerecord/lib/active_record/base.rb
  27. +56 −0 activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
  28. +7 −3 activerecord/lib/active_record/migration.rb
  29. +2 −7 activerecord/lib/active_record/railtie.rb
  30. +0 −1 activerecord/lib/active_record/relation/finder_methods.rb
  31. +42 −31 activerecord/lib/active_record/relation/query_methods.rb
  32. +35 −50 activerecord/lib/active_record/serializers/xml_serializer.rb
  33. +113 −15 activerecord/lib/active_record/transactions.rb
  34. +3 −7 activerecord/lib/rails/generators/active_record.rb
  35. +9 −0 activerecord/test/cases/associations/cascaded_eager_loading_test.rb
  36. +1 −4 activerecord/test/cases/base_test.rb
  37. +19 −0 activerecord/test/cases/migration_test.rb
  38. +7 −0 activerecord/test/cases/serialization_test.rb
  39. +244 −0 activerecord/test/cases/transaction_callbacks_test.rb
  40. +47 −14 activerecord/test/cases/transactions_test.rb
  41. +10 −2 activerecord/test/cases/xml_serialization_test.rb
  42. +4 −0 activerecord/test/models/author.rb
  43. +3 −1 activerecord/test/models/contact.rb
  44. +2 −0 activesupport/CHANGELOG
  45. +1 −1 activesupport/activesupport.gemspec
  46. +8 −6 activesupport/lib/active_support/cache/strategy/local_cache.rb
  47. +20 −22 activesupport/lib/active_support/core_ext/array/conversions.rb
  48. +13 −136 activesupport/lib/active_support/core_ext/hash/conversions.rb
  49. +56 −38 activesupport/lib/active_support/inflector/transliterate.rb
  50. +27 −18 activesupport/lib/active_support/multibyte/chars.rb
  51. +126 −1 activesupport/lib/active_support/xml_mini.rb
  52. +5 −5 activesupport/test/core_ext/array_ext_test.rb
  53. +18 −33 activesupport/test/transliterate_test.rb
  54. +14 −20 railties/guides/source/generators.textile
  55. +1 −1 railties/guides/source/index.html.erb
  56. +2 −2 railties/lib/rails/application.rb
  57. +2 −1 railties/lib/rails/application/bootstrap.rb
  58. +11 −9 railties/lib/rails/application/configuration.rb
  59. +48 −54 railties/lib/rails/commands.rb
  60. +1 −1 railties/lib/rails/commands/application.rb
  61. 0 railties/lib/rails/commands/{performance → }/benchmarker.rb
  62. 0 railties/lib/rails/commands/{performance → }/profiler.rb
  63. +2 −1 railties/lib/rails/commands/runner.rb
  64. +1 −0 railties/lib/rails/configuration.rb
  65. +1 −1 railties/lib/rails/engine.rb
  66. +22 −16 railties/lib/rails/generators/base.rb
  67. +237 −95 railties/lib/rails/generators/rails/app/app_generator.rb
  68. +2 −5 railties/lib/rails/generators/rails/app/templates/script/rails
  69. +1 −3 railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt
  70. +1 −0 railties/lib/rails/railtie.rb
  71. +1 −1 railties/railties.gemspec
  72. +4 −3 railties/test/application/middleware_test.rb
  73. +33 −0 railties/test/application/model_initialization_test.rb
  74. +3 −2 railties/test/application/paths_test.rb
  75. +14 −0 railties/test/application/rake_test.rb
  76. +2 −0 railties/test/fixtures/lib/empty_builder.rb
  77. +7 −0 railties/test/fixtures/lib/simple_builder.rb
  78. +7 −0 railties/test/fixtures/lib/tweak_builder.rb
  79. +1 −1 railties/test/generators/actions_test.rb
  80. +101 −34 railties/test/generators/app_generator_test.rb
  81. +2 −1 railties/test/generators/mailer_generator_test.rb
@@ -1,6 +1,8 @@
module Rails
module Generators
class MailerGenerator < NamedBase
+ source_root File.expand_path("../templates", __FILE__)
+
argument :actions, :type => :array, :default => [], :banner => "method method"
check_class_collision
View
@@ -1,5 +1,11 @@
*Rails 3.0.0 [beta 4/release candidate] (unreleased)*
+* OAuth 2: HTTP Token Authorization support to complement Basic and Digest Authorization. [Rick Olson]
+
+* Fixed inconsistencies in form builder and view helpers #4432 [Neeraj Singh]
+
+* Both :xml and :json renderers now forwards the given options to the model, allowing you to invoke them as render :xml => @projects, :include => :tasks [José Valim, Yehuda Katz]
+
* Renamed the field error CSS class from fieldWithErrors to field_with_errors for consistency. [Jeremy Kemper]
* Add support for shorthand routes like /projects/status(.:format) #4423 [Diego Carrion]
@@ -35,6 +35,7 @@ def self.without_modules(*modules)
RecordIdentifier,
HttpAuthentication::Basic::ControllerMethods,
HttpAuthentication::Digest::ControllerMethods,
+ HttpAuthentication::Token::ControllerMethods,
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
@@ -133,7 +133,7 @@ def filter(controller)
body = controller._save_fragment(cache_path.path, @store_options)
end
- body = controller.render_to_string(:text => cache, :layout => true) unless @cache_layout
+ body = controller.render_to_string(:text => body, :layout => true) unless @cache_layout
controller.response_body = body
controller.content_type = Mime[cache_path.extension || :html]
@@ -300,5 +300,163 @@ def opaque(secret_key)
end
end
+
+ # Makes it dead easy to do HTTP Token authentication.
+ #
+ # Simple Token example:
+ #
+ # class PostsController < ApplicationController
+ # TOKEN = "secret"
+ #
+ # before_filter :authenticate, :except => [ :index ]
+ #
+ # def index
+ # render :text => "Everyone can see me!"
+ # end
+ #
+ # def edit
+ # render :text => "I'm only accessible if you know the password"
+ # end
+ #
+ # private
+ # def authenticate
+ # authenticate_or_request_with_http_token do |token, options|
+ # token == TOKEN
+ # end
+ # end
+ # end
+ #
+ #
+ # Here is a more advanced Token example where only Atom feeds and the XML API is protected by HTTP token authentication,
+ # the regular HTML interface is protected by a session approach:
+ #
+ # class ApplicationController < ActionController::Base
+ # before_filter :set_account, :authenticate
+ #
+ # protected
+ # def set_account
+ # @account = Account.find_by_url_name(request.subdomains.first)
+ # end
+ #
+ # def authenticate
+ # case request.format
+ # when Mime::XML, Mime::ATOM
+ # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
+ # @current_user = user
+ # else
+ # request_http_token_authentication
+ # end
+ # else
+ # if session_authenticated?
+ # @current_user = @account.users.find(session[:authenticated][:user_id])
+ # else
+ # redirect_to(login_url) and return false
+ # end
+ # end
+ # end
+ # end
+ #
+ #
+ # In your integration tests, you can do something like this:
+ #
+ # def test_access_granted_from_xml
+ # get(
+ # "/notes/1.xml", nil,
+ # :authorization => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
+ # )
+ #
+ # assert_equal 200, status
+ # end
+ #
+ #
+ # On shared hosts, Apache sometimes doesn't pass authentication headers to
+ # FCGI instances. If your environment matches this description and you cannot
+ # authenticate, try this rule in your Apache setup:
+ #
+ # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
+ module Token
+
+ extend self
+
+ module ControllerMethods
+ def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
+ authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
+ end
+
+ def authenticate_with_http_token(&login_procedure)
+ Token.authenticate(self, &login_procedure)
+ end
+
+ def request_http_token_authentication(realm = "Application")
+ Token.authentication_request(self, realm)
+ end
+ end
+
+ # If token Authorization header is present, call the login procedure with
+ # the present token and options.
+ #
+ # controller - ActionController::Base instance for the current request.
+ # login_procedure - Proc to call if a token is present. The Proc should
+ # take 2 arguments:
+ # authenticate(controller) { |token, options| ... }
+ #
+ # Returns the return value of `&login_procedure` if a token is found.
+ # Returns nil if no token is found.
+ def authenticate(controller, &login_procedure)
+ token, options = token_and_options(controller.request)
+ if !token.blank?
+ login_procedure.call(token, options)
+ end
+ end
+
+ # Parses the token and options out of the token authorization header. If
+ # the header looks like this:
+ # Authorization: Token token="abc", nonce="def"
+ # Then the returned token is "abc", and the options is {:nonce => "def"}
+ #
+ # request - ActionController::Request instance with the current headers.
+ #
+ # Returns an Array of [String, Hash] if a token is present.
+ # Returns nil if no token is found.
+ def token_and_options(request)
+ if header = request.authorization.to_s[/^Token (.*)/]
+ values = $1.split(',').
+ inject({}) do |memo, value|
+ value.strip! # remove any spaces between commas and values
+ key, value = value.split(/\=\"?/) # split key=value pairs
+ value.chomp!('"') # chomp trailing " in value
+ value.gsub!(/\\\"/, '"') # unescape remaining quotes
+ memo.update(key => value)
+ end
+ [values.delete("token"), values.with_indifferent_access]
+ end
+ end
+
+ # Encodes the given token and options into an Authorization header value.
+ #
+ # token - String token.
+ # options - optional Hash of the options.
+ #
+ # Returns String.
+ def encode_credentials(token, options = {})
+ values = ["token=#{token.to_s.inspect}"]
+ options.each do |key, value|
+ values << "#{key}=#{value.to_s.inspect}"
+ end
+ "Token #{values * ", "}"
+ end
+
+ # Sets a WWW-Authenticate to let the client know a token is desired.
+ #
+ # controller - ActionController::Base instance for the outgoing response.
+ # realm - String realm to use in the header.
+ #
+ # Returns nothing.
+ def authentication_request(controller, realm)
+ controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
+ controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
+ end
+ end
+
end
end
@@ -45,7 +45,17 @@ def initialize(app, consider_all_requests_local = false)
end
def call(env)
- @app.call(env)
+ status, headers, body = @app.call(env)
+
+ # Only this middleware cares about RoutingError. So, let's just raise
+ # it here.
+ # TODO: refactor this middleware to handle the X-Cascade scenario without
+ # having to raise an exception.
+ if headers['X-Cascade'] == 'pass'
+ raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
+ end
+
+ [status, headers, body]
rescue Exception => exception
raise exception if env['action_dispatch.show_exceptions'] == false
render_exception(env, exception)
@@ -6,10 +6,6 @@
module ActionDispatch
module Routing
class RouteSet #:nodoc:
- NotFound = lambda { |env|
- raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
- }
-
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
class Dispatcher #:nodoc:
@@ -224,7 +220,6 @@ def draw(&block)
def finalize!
return if @finalized
@finalized = true
- @set.add_route(NotFound)
@set.freeze
end
@@ -589,56 +589,50 @@ def initialize(datetime, options = {}, html_options = {})
@options = options.dup
@html_options = html_options.dup
@datetime = datetime
+ @options[:datetime_separator] ||= ' &mdash; '
+ @options[:time_separator] ||= ' : '
end
def select_datetime
- # TODO: Remove tag conditional
- # Ideally we could just join select_date and select_date for the tag case
+ order = date_order.dup
+ order -= [:hour, :minute, :second]
+ @options[:discard_year] ||= true unless order.include?(:year)
+ @options[:discard_month] ||= true unless order.include?(:month)
+ @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
+ @options[:discard_minute] ||= true if @options[:discard_hour]
+ @options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
+
+ # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
+ # valid (otherwise it could be 31 and february wouldn't be a valid date)
+ if @datetime && @options[:discard_day] && !@options[:discard_month]
+ @datetime = @datetime.change(:day => 1)
+ end
+
if @options[:tag] && @options[:ignore_date]
select_time
- elsif @options[:tag]
- order = date_order.dup
- order -= [:hour, :minute, :second]
-
- @options[:discard_year] ||= true unless order.include?(:year)
- @options[:discard_month] ||= true unless order.include?(:month)
- @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
- @options[:discard_minute] ||= true if @options[:discard_hour]
- @options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
-
- # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
- # valid (otherwise it could be 31 and february wouldn't be a valid date)
- if @datetime && @options[:discard_day] && !@options[:discard_month]
- @datetime = @datetime.change(:day => 1)
- end
-
+ else
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
order += [:hour, :minute, :second] unless @options[:discard_hour]
build_selects_from_types(order)
- else
- "#{select_date}#{@options[:datetime_separator]}#{select_time}".html_safe
end
end
def select_date
order = date_order.dup
- # TODO: Remove tag conditional
- if @options[:tag]
- @options[:discard_hour] = true
- @options[:discard_minute] = true
- @options[:discard_second] = true
+ @options[:discard_hour] = true
+ @options[:discard_minute] = true
+ @options[:discard_second] = true
- @options[:discard_year] ||= true unless order.include?(:year)
- @options[:discard_month] ||= true unless order.include?(:month)
- @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
+ @options[:discard_year] ||= true unless order.include?(:year)
+ @options[:discard_month] ||= true unless order.include?(:month)
+ @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
- # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
- # valid (otherwise it could be 31 and february wouldn't be a valid date)
- if @datetime && @options[:discard_day] && !@options[:discard_month]
- @datetime = @datetime.change(:day => 1)
- end
+ # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
+ # valid (otherwise it could be 31 and february wouldn't be a valid date)
+ if @datetime && @options[:discard_day] && !@options[:discard_month]
+ @datetime = @datetime.change(:day => 1)
end
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
@@ -649,15 +643,12 @@ def select_date
def select_time
order = []
- # TODO: Remove tag conditional
- if @options[:tag]
- @options[:discard_month] = true
- @options[:discard_year] = true
- @options[:discard_day] = true
- @options[:discard_second] ||= true unless @options[:include_seconds]
+ @options[:discard_month] = true
+ @options[:discard_year] = true
+ @options[:discard_day] = true
+ @options[:discard_second] ||= true unless @options[:include_seconds]
- order += [:year, :month, :day] unless @options[:ignore_date]
- end
+ order += [:year, :month, :day] unless @options[:ignore_date]
order += [:hour, :minute]
order << :second if @options[:include_seconds]
@@ -937,10 +928,8 @@ def datetime_selector(options, html_options)
options[:include_position] = true
options[:prefix] ||= @object_name
options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
- options[:datetime_separator] ||= ' &mdash; '
- options[:time_separator] ||= ' : '
- DateTimeSelector.new(datetime, options.merge(:tag => true), html_options)
+ DateTimeSelector.new(datetime, options, html_options)
end
def default_datetime(options)
@@ -596,10 +596,8 @@ def convert_options_to_data_attributes(options, html_options)
html_options = {} if html_options.nil?
html_options = html_options.stringify_keys
- if (options.is_a?(Hash) && options.key?('remote')) || (html_options.is_a?(Hash) && html_options.key?('remote'))
+ if (options.is_a?(Hash) && options.key?('remote') && options.delete('remote')) || (html_options.is_a?(Hash) && html_options.key?('remote') && html_options.delete('remote'))
html_options['data-remote'] = 'true'
- options.delete('remote') if options.is_a?(Hash)
- html_options.delete('remote') if html_options.is_a?(Hash)
end
confirm = html_options.delete("confirm")
Oops, something went wrong.

0 comments on commit efba1d4

Please sign in to comment.