diff options
36 files changed, 371 insertions, 222 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 7022c04a9a..6f49e130d8 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -820,7 +820,7 @@ module ActionMailer # Set configure delivery behavior wrap_delivery_behavior!(headers.delete(:delivery_method), headers.delete(:delivery_method_options)) - # Assign all headers except parts_order, content_type and body + # Assign all headers except parts_order, content_type, body, template_name, and template_path assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path) assignable.each { |k, v| m[k] = v } diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 032275ac64..bbb38cf8fc 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -94,7 +94,7 @@ module ActionController end def has_basic_credentials?(request) - request.authorization.present? && (auth_scheme(request) == 'Basic') + request.authorization.present? && (auth_scheme(request).downcase == 'basic') end def user_name_and_password(request) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index ab13fb14ce..a12f60e20d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -61,7 +61,9 @@ module ActionDispatch attr_reader :requirements, :conditions, :defaults attr_reader :to, :default_controller, :default_action, :as, :anchor - def self.build(scope, set, path, as, options) + def self.build(scope, set, path, as, controller, default_action, to, options) + formatted = options.delete(:format) { scope[:options] && scope[:options][:format] } + options = scope[:options].merge(options) if scope[:options] options.delete :only @@ -69,50 +71,50 @@ module ActionDispatch options.delete :shallow_path options.delete :shallow_prefix options.delete :shallow + options.delete :format - defaults = (scope[:defaults] || {}).merge options.delete(:defaults) || {} + defaults = (scope[:defaults] || {}).dup + scope_constraints = scope[:constraints] || {} - new scope, set, path, defaults, as, options + new set, path, defaults, as, controller, default_action, scope[:module], to, formatted, scope_constraints, scope[:blocks] || [], options end - def initialize(scope, set, path, defaults, as, options) + def initialize(set, path, defaults, as, controller, default_action, modyoule, to, formatted, scope_constraints, blocks, options) @requirements, @conditions = {}, {} @defaults = defaults @set = set - @to = options.delete :to - @default_controller = options.delete(:controller) || scope[:controller] - @default_action = options.delete(:action) || scope[:action] + @to = to + @default_controller = controller + @default_action = default_action @as = as @anchor = options.delete :anchor - formatted = options.delete :format via = Array(options.delete(:via) { [] }) - options_constraints = options.delete :constraints + options_constraints = options.delete(:constraints) || {} path = normalize_path! path, formatted ast = path_ast path path_params = path_params ast - options = normalize_options!(options, formatted, path_params, ast, scope[:module]) - - - split_constraints(path_params, scope[:constraints]) if scope[:constraints] - constraints = constraints(options, path_params) - - split_constraints path_params, constraints + options = normalize_options!(options, formatted, path_params, ast, modyoule) - @blocks = blocks(options_constraints, scope[:blocks]) + constraints = scope_constraints.merge constraints(options, path_params) if options_constraints.is_a?(Hash) - split_constraints path_params, options_constraints options_constraints.each do |key, default| if URL_OPTIONS.include?(key) && (String === default || Fixnum === default) @defaults[key] ||= default end end + @blocks = blocks + constraints.merge! options_constraints + else + @blocks = blocks(options_constraints) end + split_constraints path_params, constraints + normalize_format!(formatted) @conditions[:path_info] = path @@ -217,12 +219,6 @@ module ActionDispatch end end - def verify_callable_constraint(callable_constraint) - unless callable_constraint.respond_to?(:call) || callable_constraint.respond_to?(:matches?) - raise ArgumentError, "Invalid constraint: #{callable_constraint.inspect} must respond to :call or :matches?" - end - end - def add_request_method(via, conditions) return if via == [:all] @@ -302,13 +298,11 @@ module ActionDispatch yield end - def blocks(options_constraints, scope_blocks) - if options_constraints && !options_constraints.is_a?(Hash) - verify_callable_constraint(options_constraints) - [options_constraints] - else - scope_blocks || [] + def blocks(callable_constraint) + unless callable_constraint.respond_to?(:call) || callable_constraint.respond_to?(:matches?) + raise ArgumentError, "Invalid constraint: #{callable_constraint.inspect} must respond to :call or :matches?" end + [callable_constraint] end def constraints(options, path_params) @@ -447,10 +441,10 @@ module ActionDispatch # resources :user, param: :name # # You can override <tt>ActiveRecord::Base#to_param</tt> of a related - # model to constructe an URL. + # model to construct an URL: # # class User < ActiveRecord::Base - # def to_param # overridden + # def to_param # name # end # end @@ -684,7 +678,11 @@ module ActionDispatch def map_method(method, args, &block) options = args.extract_options! options[:via] = method - match(*args, options, &block) + if options.key?(:defaults) + defaults(options.delete(:defaults)) { match(*args, options, &block) } + else + match(*args, options, &block) + end self end end @@ -787,8 +785,8 @@ module ActionDispatch end if options[:constraints].is_a?(Hash) - defaults = options[:constraints].select do - |k, v| URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) + defaults = options[:constraints].select do |k, v| + URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) end (options[:defaults] ||= {}).reverse_merge!(defaults) @@ -822,9 +820,11 @@ module ActionDispatch # controller "food" do # match "bacon", action: :bacon, via: :get # end - def controller(controller, options={}) - options[:controller] = controller - scope(options) { yield } + def controller(controller) + @scope = @scope.new(controller: controller) + yield + ensure + @scope = @scope.parent end # Scopes routes to a specific namespace. For example: @@ -870,13 +870,14 @@ module ActionDispatch defaults = { module: path, - path: options.fetch(:path, path), as: options.fetch(:as, path), shallow_path: options.fetch(:path, path), shallow_prefix: options.fetch(:as, path) } - scope(defaults.merge!(options)) { yield } + path_scope(options.delete(:path) { path }) do + scope(defaults.merge!(options)) { yield } + end end # === Parameter Restriction @@ -944,7 +945,10 @@ module ActionDispatch # end # Using this, the +:id+ parameter here will default to 'home'. def defaults(defaults = {}) - scope(:defaults => defaults) { yield } + @scope = @scope.new(defaults: merge_defaults_scope(@scope[:defaults], defaults)) + yield + ensure + @scope = @scope.parent end private @@ -1056,14 +1060,14 @@ module ActionDispatch class Resource #:nodoc: attr_reader :controller, :path, :options, :param - def initialize(entities, api_only = false, options = {}) + def initialize(entities, api_only, shallow, options = {}) @name = entities.to_s @path = (options[:path] || @name).to_s @controller = (options[:controller] || @name).to_s @as = options[:as] @param = (options[:param] || :id).to_sym @options = options - @shallow = false + @shallow = shallow @api_only = api_only end @@ -1129,17 +1133,15 @@ module ActionDispatch "#{path}/:#{nested_param}" end - def shallow=(value) - @shallow = value - end - def shallow? @shallow end + + def singleton?; false; end end class SingletonResource < Resource #:nodoc: - def initialize(entities, api_only, options) + def initialize(entities, api_only, shallow, options) super @as = nil @controller = (options[:controller] || plural).to_s @@ -1167,6 +1169,8 @@ module ActionDispatch alias :member_scope :path alias :nested_scope :path + + def singleton?; true; end end def resources_path_names(options) @@ -1201,20 +1205,22 @@ module ActionDispatch return self end - resource_scope(:resource, SingletonResource.new(resources.pop, api_only?, options)) do - yield if block_given? + with_scope_level(:resource) do + resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], options)) do + yield if block_given? - concerns(options[:concerns]) if options[:concerns] + concerns(options[:concerns]) if options[:concerns] - collection do - post :create - end if parent_resource.actions.include?(:create) + collection do + post :create + end if parent_resource.actions.include?(:create) - new do - get :new - end if parent_resource.actions.include?(:new) + new do + get :new + end if parent_resource.actions.include?(:new) - set_member_mappings_for_resource + set_member_mappings_for_resource + end end self @@ -1359,21 +1365,23 @@ module ActionDispatch return self end - resource_scope(:resources, Resource.new(resources.pop, api_only?, options)) do - yield if block_given? + with_scope_level(:resources) do + resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], options)) do + yield if block_given? - concerns(options[:concerns]) if options[:concerns] + concerns(options[:concerns]) if options[:concerns] - collection do - get :index if parent_resource.actions.include?(:index) - post :create if parent_resource.actions.include?(:create) - end + collection do + get :index if parent_resource.actions.include?(:index) + post :create if parent_resource.actions.include?(:create) + end - new do - get :new - end if parent_resource.actions.include?(:new) + new do + get :new + end if parent_resource.actions.include?(:new) - set_member_mappings_for_resource + set_member_mappings_for_resource + end end self @@ -1479,7 +1487,7 @@ module ActionDispatch end def shallow? - parent_resource.instance_of?(Resource) && @scope[:shallow] + !parent_resource.singleton? && @scope[:shallow] end # Matches a url pattern to one or more routes. @@ -1523,48 +1531,65 @@ module ActionDispatch options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}" end - paths.each do |_path| - route_options = options.dup - route_options[:path] ||= _path if _path.is_a?(String) + controller = options.delete(:controller) || @scope[:controller] + option_path = options.delete :path + to = options.delete :to - path_without_format = _path.to_s.sub(/\(\.:format\)$/, '') - if using_match_shorthand?(path_without_format, route_options) - route_options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1') - route_options[:to].tr!("-", "_") - end + path_types = paths.group_by(&:class) + path_types.fetch(String, []).each do |_path| + route_options = options.dup + process_path(route_options, controller, _path, option_path || _path, to) + end - decomposed_match(_path, route_options) + path_types.fetch(Symbol, []).each do |action| + route_options = options.dup + decomposed_match(action, controller, route_options, option_path, to) end + self end - def using_match_shorthand?(path, options) - path && (options[:to] || options[:action]).nil? && path =~ %r{^/?[-\w]+/[-\w/]+$} + def process_path(options, controller, path, option_path, to) + path_without_format = path.sub(/\(\.:format\)$/, '') + if using_match_shorthand?(path_without_format, to, options[:action]) + to ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1') + to.tr!("-", "_") + end + + decomposed_match(path, controller, options, option_path, to) end - def decomposed_match(path, options) # :nodoc: + def using_match_shorthand?(path, to, action) + return false if to || action + + path =~ %r{^/?[-\w]+/[-\w/]+$} + end + + def decomposed_match(path, controller, options, _path, to) # :nodoc: if on = options.delete(:on) - send(on) { decomposed_match(path, options) } + send(on) { decomposed_match(path, controller, options, _path, to) } else case @scope.scope_level when :resources - nested { decomposed_match(path, options) } + nested { decomposed_match(path, controller, options, _path, to) } when :resource - member { decomposed_match(path, options) } + member { decomposed_match(path, controller, options, _path, to) } else - add_route(path, options) + add_route(path, controller, options, _path, to) end end end - def add_route(action, options) # :nodoc: - path = path_for_action(action, options.delete(:path)) + def add_route(action, controller, options, _path, to) # :nodoc: + path = path_for_action(action, _path) raise ArgumentError, "path is required" if path.blank? action = action.to_s + default_action = options.delete(:action) || @scope[:action] + if action =~ /^[\w\-\/]+$/ - options[:action] ||= action.tr('-', '_') unless action.include?("/") + default_action ||= action.tr('-', '_') unless action.include?("/") else action = nil end @@ -1575,7 +1600,7 @@ module ActionDispatch name_for_action(options.delete(:as), action) end - mapping = Mapping.build(@scope, @set, URI.parser.escape(path), as, options) + mapping = Mapping.build(@scope, @set, URI.parser.escape(path), as, controller, default_action, to, options) app, conditions, requirements, defaults, as, anchor = mapping.to_route @set.add_route(app, conditions, requirements, defaults, as, anchor) end @@ -1591,7 +1616,7 @@ module ActionDispatch if @scope.resources? with_scope_level(:root) do - scope(parent_resource.path) do + path_scope(parent_resource.path) do super(options) end end @@ -1667,18 +1692,6 @@ module ActionDispatch @scope.nested? end - def with_exclusive_scope - begin - @scope = @scope.new(:as => nil, :path => nil) - - with_scope_level(:exclusive) do - yield - end - ensure - @scope = @scope.parent - end - end - def with_scope_level(kind) @scope = @scope.new_level(kind) yield @@ -1686,16 +1699,11 @@ module ActionDispatch @scope = @scope.parent end - def resource_scope(kind, resource) #:nodoc: - resource.shallow = @scope[:shallow] + def resource_scope(resource) #:nodoc: @scope = @scope.new(:scope_level_resource => resource) - @nesting.push(resource) - with_scope_level(kind) do - controller_scope(parent_resource.resource_scope) { yield } - end + controller(resource.resource_scope) { yield } ensure - @nesting.pop @scope = @scope.parent end @@ -1708,12 +1716,10 @@ module ActionDispatch options end - def nesting_depth #:nodoc: - @nesting.size - end - def shallow_nesting_depth #:nodoc: - @nesting.count(&:shallow?) + @scope.find_all { |node| + node.frame[:scope_level_resource] + }.count { |node| node.frame[:scope_level_resource].shallow? } end def param_constraint? #:nodoc: @@ -1739,16 +1745,17 @@ module ActionDispatch end def path_for_action(action, path) #:nodoc: - if path.blank? && canonical_action?(action) + return "#{@scope[:path]}/#{path}" if path + + if canonical_action?(action) @scope[:path].to_s else - "#{@scope[:path]}/#{action_path(action, path)}" + "#{@scope[:path]}/#{action_path(action)}" end end - def action_path(name, path = nil) #:nodoc: - name = name.to_sym if name.is_a?(String) - path || @scope[:path_names][name] || name.to_s + def action_path(name) #:nodoc: + @scope[:path_names][name.to_sym] || name end def prefix_name_for_action(as, action) #:nodoc: @@ -1806,13 +1813,6 @@ module ActionDispatch end private - def controller_scope(controller) - @scope = @scope.new(controller: controller) - yield - ensure - @scope = @scope.parent - end - def path_scope(path) @scope = @scope.new(path: merge_path_scope(@scope[:path], path)) yield @@ -1936,7 +1936,7 @@ module ActionDispatch attr_reader :parent, :scope_level - def initialize(hash, parent = {}, scope_level = nil) + def initialize(hash, parent = NULL, scope_level = nil) @hash = hash @parent = parent @scope_level = scope_level @@ -1984,23 +1984,34 @@ module ActionDispatch end def new_level(level) - self.class.new(self, self, level) + self.class.new(frame, self, level) end - def fetch(key, &block) - @hash.fetch(key, &block) + def [](key) + scope = find { |node| node.frame.key? key } + scope && scope.frame[key] end - def [](key) - @hash.fetch(key) { @parent[key] } + include Enumerable + + def each + node = self + loop do + break if node.equal? NULL + yield node + node = node.parent + end end + + def frame; @hash; end + + NULL = Scope.new(nil, nil) end def initialize(set) #:nodoc: @set = set @scope = Scope.new({ :path_names => @set.resources_path_names }) @concerns = {} - @nesting = [] end include Base diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index ed3632007d..0a5e5402b9 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -100,6 +100,14 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_no_match(/\n/, result) end + test "succesful authentication with uppercase authorization scheme" do + @request.env['HTTP_AUTHORIZATION'] = "BASIC #{::Base64.encode64("lifo:world")}" + get :index + + assert_response :success + assert_equal 'Hello Secret', @response.body, 'Authentication failed when authorization scheme BASIC' + end + test "authentication request without credential" do get :display diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb index aed1d914f9..b0815c41c8 100644 --- a/actionpack/test/dispatch/mapper_test.rb +++ b/actionpack/test/dispatch/mapper_test.rb @@ -41,8 +41,8 @@ module ActionDispatch end def test_mapping_requirements - options = { :controller => 'foo', :action => 'bar', :via => :get } - m = Mapper::Mapping.build({}, FakeSet.new, '/store/:name(*rest)', nil, options) + options = { :via => :get } + m = Mapper::Mapping.build({}, FakeSet.new, '/store/:name(*rest)', nil, 'foo', 'bar', nil, options) _, _, requirements, _ = m.to_route assert_equal(/.+?/, requirements[:rest]) end diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 55a2e155d1..edc78118fb 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add a break_sequence option to word_wrap so you can specify a custom break. + + * Mauricio Gomez * + * Add wildcard matching to explicit dependencies. Turns: diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index 3beb7fefb1..6ac8185b0f 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -31,26 +31,33 @@ module ActionView # stylesheet_link_tag("application") # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" /> # - # Browsers typically open at most two simultaneous connections to a single - # host, which means your assets often have to wait for other assets to finish - # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the - # +asset_host+. For example, "assets%d.example.com". If that wildcard is - # present Rails distributes asset requests among the corresponding four hosts - # "assets0.example.com", ..., "assets3.example.com". With this trick browsers - # will open eight simultaneous connections rather than two. + # Browsers open a limited number of simultaneous connections to a single + # host. The exact number varies by browser and version. This limit may cause + # some asset downloads to wait for previous assets to finish before they can + # begin. You can use the <tt>%d</tt> wildcard in the +asset_host+ to + # distribute the requests over four hosts. For example, + # <tt>assets%d.example.com<tt> will spread the asset requests over + # "assets0.example.com", ..., "assets3.example.com". # # image_tag("rails.png") # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" /> # stylesheet_link_tag("application") # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" /> # - # To do this, you can either setup four actual hosts, or you can use wildcard - # DNS to CNAME the wildcard to a single asset host. You can read more about - # setting up your DNS CNAME records from your ISP. + # This may improve the asset loading performance of your application. + # It is also possible the combination of additional connection overhead + # (DNS, SSL) and the overall browser connection limits may result in this + # solution being slower. You should be sure to measure your actual + # performance across targeted browers both before and after this change. + # + # To implement the corresponding hosts you can either setup four actual + # hosts or use wildcard DNS to CNAME the wilcard to a single asset host. + # You can read more about setting up your DNS CNAME records from your ISP. # # Note: This is purely a browser performance optimization and is not meant # for server load balancing. See http://www.die.net/musings/page_load_time/ - # for background. + # for background and http://www.browserscope.org/?category=network for + # connection limit data. # # Alternatively, you can exert more control over the asset host by setting # +asset_host+ to a proc like this: diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb index 6a3d01667d..432693bc23 100644 --- a/actionview/lib/action_view/helpers/text_helper.rb +++ b/actionview/lib/action_view/helpers/text_helper.rb @@ -250,12 +250,15 @@ module ActionView # # word_wrap('Once upon a time', line_width: 1) # # => Once\nupon\na\ntime - def word_wrap(text, options = {}) - line_width = options.fetch(:line_width, 80) - + # + # You can also specify a custom +break_sequence+ ("\n" by default) + # + # word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n") + # # => Once\r\nupon\r\na\r\ntime + def word_wrap(text, line_width: 80, break_sequence: "\n") text.split("\n").collect! do |line| - line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line - end * "\n" + line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").strip : line + end * break_sequence end # Returns +text+ transformed into HTML using simple formatting rules. diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb index c4bc26ca8a..f6b5696a13 100644 --- a/actionview/lib/action_view/test_case.rb +++ b/actionview/lib/action_view/test_case.rb @@ -263,9 +263,15 @@ module ActionView end def method_missing(selector, *args) - if @controller.respond_to?(:_routes) && - ( @controller._routes.named_routes.route_defined?(selector) || - @controller._routes.mounted_helpers.method_defined?(selector) ) + begin + routes = @controller.respond_to?(:_routes) && @controller._routes + rescue + # Dont call routes, if there is an error on _routes call + end + + if routes && + ( routes.named_routes.route_defined?(selector) || + routes.mounted_helpers.method_defined?(selector) ) @controller.__send__(selector, *args) else super diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb index 5791f33069..fae1965ffa 100644 --- a/actionview/test/template/text_helper_test.rb +++ b/actionview/test/template/text_helper_test.rb @@ -366,6 +366,10 @@ class TextHelperTest < ActionView::TestCase assert_equal options, passed_options end + def test_word_wrap_with_custom_break_sequence + assert_equal("1234567890\r\n1234567890\r\n1234567890", word_wrap("1234567890 " * 3, line_width: 2, break_sequence: "\r\n")) + end + def test_pluralization assert_equal("1 count", pluralize(1, "count")) assert_equal("2 counts", pluralize(2, "count")) diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md index 1bd97eed38..d8944a5338 100644 --- a/activejob/CHANGELOG.md +++ b/activejob/CHANGELOG.md @@ -1,3 +1,8 @@ +* `assert_enqueued_with` and `assert_performed_with` now returns the matched + job instance for further assertions. + + *Jean Boussier* + * Include I18n.locale into job serialization/deserialization and use it around `perform`. diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb index e71a3b4f85..74a12884ff 100644 --- a/activejob/lib/active_job/test_helper.rb +++ b/activejob/lib/active_job/test_helper.rb @@ -233,10 +233,11 @@ module ActiveJob args.assert_valid_keys(:job, :args, :at, :queue) serialized_args = serialize_args_for_assertion(args) yield - matching_job = enqueued_jobs.any? do |job| + matching_job = enqueued_jobs.find do |job| serialized_args.all? { |key, value| value == job[key] } end assert matching_job, "No enqueued job found with #{args}" + instanciate_job(matching_job) ensure queue_adapter.enqueued_jobs = original_enqueued_jobs + enqueued_jobs end @@ -254,10 +255,11 @@ module ActiveJob args.assert_valid_keys(:job, :args, :at, :queue) serialized_args = serialize_args_for_assertion(args) perform_enqueued_jobs { yield } - matching_job = performed_jobs.any? do |job| + matching_job = performed_jobs.find do |job| serialized_args.all? { |key, value| value == job[key] } end assert matching_job, "No performed job found with #{args}" + instanciate_job(matching_job) ensure queue_adapter.performed_jobs = original_performed_jobs + performed_jobs end @@ -311,6 +313,13 @@ module ActiveJob end serialized_args end + + def instanciate_job(payload) + job = payload[:job].new(*payload[:args]) + job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at) + job.queue_name = payload[:queue] + job + end end end end diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb index 04c4c446e2..8c60f037d2 100644 --- a/activejob/test/cases/test_helper_test.rb +++ b/activejob/test/cases/test_helper_test.rb @@ -165,6 +165,17 @@ class EnqueuedJobsTest < ActiveJob::TestCase end end + def test_assert_enqueued_job_returns + job = assert_enqueued_with(job: LoggingJob) do + LoggingJob.set(wait_until: 5.minutes.from_now).perform_later(1, 2, 3) + end + + assert_instance_of LoggingJob, job + assert_in_delta 5.minutes.from_now, job.scheduled_at, 1 + assert_equal 'default', job.queue_name + assert_equal [1, 2, 3], job.arguments + end + def test_assert_enqueued_job_failure assert_raise ActiveSupport::TestCase::Assertion do assert_enqueued_with(job: LoggingJob, queue: 'default') do @@ -397,6 +408,17 @@ class PerformedJobsTest < ActiveJob::TestCase end end + def test_assert_performed_job_returns + job = assert_performed_with(job: NestedJob, queue: 'default') do + NestedJob.perform_later + end + + assert_instance_of NestedJob, job + assert_nil job.scheduled_at + assert_equal [], job.arguments + assert_equal 'default', job.queue_name + end + def test_assert_performed_job_failure assert_raise ActiveSupport::TestCase::Assertion do assert_performed_with(job: LoggingJob, at: Date.tomorrow.noon, queue: 'default') do diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 9033ec0dca..77e4ce3afe 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -342,7 +342,7 @@ module ActiveModel private # The methods +method_missing+ and +respond_to?+ of this module are # invoked often in a typical rails, both of which invoke the method - # +match_attribute_method?+. The latter method iterates through an + # +matched_attribute_method+. The latter method iterates through an # array doing regular expression matches, which results in a lot of # object creations. Most of the time it returns a +nil+ match. As the # match result is always the same given a +method_name+, this cache is @@ -429,7 +429,7 @@ module ActiveModel if respond_to_without_attributes?(method, true) super else - match = match_attribute_method?(method.to_s) + match = matched_attribute_method(method.to_s) match ? attribute_missing(match, *args, &block) : super end end @@ -454,7 +454,7 @@ module ActiveModel # but found among all methods. Which means that the given method is private. false else - !match_attribute_method?(method.to_s).nil? + !matched_attribute_method(method.to_s).nil? end end @@ -466,7 +466,7 @@ module ActiveModel private # Returns a struct representing the matching attribute method. # The struct's attributes are prefix, base and suffix. - def match_attribute_method?(method_name) + def matched_attribute_method(method_name) matches = self.class.send(:attribute_method_matchers_matching, method_name) matches.detect { |match| attribute_method?(match.attr_name) } end diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c1ea8a3252..c3b2d157f3 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* `ActiveRecord::Tasks::PostgreSQLDatabaseTasks` fail if shellout to + postgresql commands (like `pg_dump`) is not successful. + + *Bryan Paxton*, *Nate Berkopec* + * Add `ActiveRecord::Relation#in_batches` to work with records and relations in batches. diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index ffd9c9d6fc..b18d99d54e 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -46,7 +46,7 @@ module ActiveRecord::Associations::Builder join_model = Class.new(ActiveRecord::Base) { class << self; - attr_accessor :class_resolver + attr_accessor :left_model attr_accessor :name attr_accessor :table_name_resolver attr_accessor :left_reflection @@ -58,7 +58,7 @@ module ActiveRecord::Associations::Builder end def self.compute_type(class_name) - class_resolver.compute_type class_name + left_model.compute_type class_name end def self.add_left_association(name, options) @@ -72,11 +72,15 @@ module ActiveRecord::Associations::Builder self.right_reflection = _reflect_on_association(rhs_name) end + def self.retrieve_connection + left_model.retrieve_connection + end + } join_model.name = "HABTM_#{association_name.to_s.camelize}" join_model.table_name_resolver = habtm - join_model.class_resolver = lhs_model + join_model.left_model = lhs_model join_model.add_left_association :left_side, anonymous_class: lhs_model join_model.add_right_association association_name, belongs_to_options(options) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 7fb899c242..0862306749 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -385,27 +385,27 @@ module ActiveRecord # # For example: # - # class PostsController < ActionController::Base - # after_action :print_accessed_fields, only: :index + # class PostsController < ActionController::Base + # after_action :print_accessed_fields, only: :index # - # def index - # @posts = Post.all - # end + # def index + # @posts = Post.all + # end # - # private + # private # - # def print_accessed_fields - # p @posts.first.accessed_fields + # def print_accessed_fields + # p @posts.first.accessed_fields + # end # end - # end # # Which allows you to quickly change your code to: # - # class PostsController < ActionController::Base - # def index - # @posts = Post.select(:id, :title, :author_id, :updated_at) + # class PostsController < ActionController::Base + # def index + # @posts = Post.select(:id, :title, :author_id, :updated_at) + # end # end - # end def accessed_fields @attributes.accessed end diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index e03bf5945d..60eecab0d0 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -11,7 +11,7 @@ module ActiveRecord # serialized object must be of that class on assignment and retrieval. # Otherwise <tt>SerializationTypeMismatch</tt> will be raised. # - # Empty objects as +{}+, in the case of +Hash+, or +[]+, in the case of + # Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of # +Array+, will always be persisted as null. # # Keep in mind that database adapters handle certain serialization tasks diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index c7c769b283..ccdbebbc77 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -206,7 +206,8 @@ module ActiveRecord # == Ordering callbacks # # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+ - # callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option. + # callback (+log_children+ in this case) should be executed before the children get destroyed by the + # <tt>dependent: destroy</tt> option. # # Let's look at the code below: # diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb index d7da95c8a9..55f839444b 100644 --- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb @@ -1,5 +1,3 @@ -require 'shellwords' - module ActiveRecord module Tasks # :nodoc: class PostgreSQLDatabaseTasks # :nodoc: @@ -55,19 +53,22 @@ module ActiveRecord when String ActiveRecord::Base.dump_schemas end + + args = ['-i', '-s', '-x', '-O', '-f', filename] unless search_path.blank? - search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ") + args << search_path.split(',').map do |part| + "--schema=#{part.strip}" + end.join(' ') end - - command = "pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(configuration['database'])}" - raise 'Error dumping database' unless Kernel.system(command) - + args << configuration['database'] + run_cmd('pg_dump', args, 'dumping') File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" } end def structure_load(filename) set_psql_env - Kernel.system("psql -X -q -f #{Shellwords.escape(filename)} #{configuration['database']}") + args = [ '-q', '-f', filename, configuration['database'] ] + run_cmd('psql', args, 'loading' ) end private @@ -93,6 +94,17 @@ module ActiveRecord ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password'] ENV['PGUSER'] = configuration['username'].to_s if configuration['username'] end + + def run_cmd(cmd, args, action) + fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args) + end + + def run_cmd_error(cmd, args, action) + msg = "failed to execute:\n" + msg << "#{cmd} #{args.join(' ')}\n\n" + msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n" + msg + end end end 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 abbe37ab38..7718b29125 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 @@ -3,6 +3,7 @@ require 'models/developer' require 'models/computer' require 'models/project' require 'models/company' +require 'models/course' require 'models/customer' require 'models/order' require 'models/categorization' @@ -14,6 +15,7 @@ require 'models/tagging' require 'models/parrot' require 'models/person' require 'models/pirate' +require 'models/professor' require 'models/treasure' require 'models/price_estimate' require 'models/club' @@ -923,4 +925,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_deprecated { developer.projects(true) } end + + def test_alternate_database + professor = Professor.create(name: "Plum") + course = Course.create(name: "Forensics") + assert_equal 0, professor.courses.count + assert_nothing_raised do + professor.courses << course + end + assert_equal 1, professor.courses.count + end end diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index 084302cde5..184ff7fc63 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -204,7 +204,7 @@ module ActiveRecord end def test_structure_dump - Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} my-app-db").returns(true) + Kernel.expects(:system).with('pg_dump', '-i', '-s', '-x', '-O', '-f', @filename, 'my-app-db').returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename) end @@ -212,7 +212,7 @@ module ActiveRecord def test_structure_dump_with_schema_search_path @configuration['schema_search_path'] = 'foo,bar' - Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} --schema=foo --schema=bar my-app-db").returns(true) + Kernel.expects(:system).with('pg_dump', '-i', '-s', '-x', '-O', '-f', @filename, '--schema=foo --schema=bar', 'my-app-db').returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename) end @@ -220,7 +220,7 @@ module ActiveRecord def test_structure_dump_with_schema_search_path_and_dump_schemas_all @configuration['schema_search_path'] = 'foo,bar' - Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} my-app-db").returns(true) + Kernel.expects(:system).with("pg_dump", '-i', '-s', '-x', '-O', '-f', @filename, 'my-app-db').returns(true) with_dump_schemas(:all) do ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename) @@ -228,7 +228,7 @@ module ActiveRecord end def test_structure_dump_with_dump_schemas_string - Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} --schema=foo --schema=bar my-app-db").returns(true) + Kernel.expects(:system).with("pg_dump", '-i', '-s', '-x', '-O', '-f', @filename, '--schema=foo --schema=bar', "my-app-db").returns(true) with_dump_schemas('foo,bar') do ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename) @@ -261,14 +261,14 @@ module ActiveRecord def test_structure_load filename = "awesome-file.sql" - Kernel.expects(:system).with("psql -X -q -f #{filename} my-app-db") + Kernel.expects(:system).with('psql', '-q', '-f', filename, @configuration['database']).returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) end def test_structure_load_accepts_path_with_spaces filename = "awesome file.sql" - Kernel.expects(:system).with("psql -X -q -f awesome\\ file.sql my-app-db") + Kernel.expects(:system).with('psql', '-q', '-f', filename, @configuration['database']).returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) end diff --git a/activerecord/test/models/professor.rb b/activerecord/test/models/professor.rb new file mode 100644 index 0000000000..7654eda0ef --- /dev/null +++ b/activerecord/test/models/professor.rb @@ -0,0 +1,5 @@ +require_dependency 'models/arunit2_model' + +class Professor < ARUnit2Model + has_and_belongs_to_many :courses +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 7bab675b2a..1fa824bf1d 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -939,3 +939,12 @@ end College.connection.create_table :colleges, force: true do |t| t.column :name, :string, null: false end + +Professor.connection.create_table :professors, force: true do |t| + t.column :name, :string, null: false +end + +Professor.connection.create_table :courses_professors, id: false, force: true do |t| + t.references :course + t.references :professor +end diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb index d11fd9cfc1..c5334e8596 100644 --- a/activerecord/test/support/connection.rb +++ b/activerecord/test/support/connection.rb @@ -1,6 +1,7 @@ require 'active_support/logger' require 'models/college' require 'models/course' +require 'models/professor' module ARTest def self.connection_name diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 7148f289bb..eaaa3df061 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* Deprecate `:prefix` option of `number_to_human_size` with no replacement. + + *Jean Boussier* + * Fix `TimeWithZone#eql?` to properly handle `TimeWithZone` created from `DateTime`: twz = DateTime.now.in_time_zone twz.eql?(twz.dup) => true diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index c5d35b84f0..0371f760b0 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -92,7 +92,7 @@ module ActiveSupport # hash = ActiveSupport::HashWithIndifferentAccess.new # hash[:key] = 'value' # - # This value can be later fetched using either +:key+ or +'key'+. + # This value can be later fetched using either +:key+ or <tt>'key'</tt>. def []=(key, value) regular_writer(convert_key(key), convert_value(value, for: :assignment)) end diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 6bf8c7d5de..0131fe2572 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -111,7 +111,7 @@ module ActiveSupport end end - class Timed < Evented + class Timed < Evented # :nodoc: def publish(name, *args) @delegate.call name, *args end diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb index 258d9b34e1..38a9ce361d 100644 --- a/activesupport/lib/active_support/number_helper.rb +++ b/activesupport/lib/active_support/number_helper.rb @@ -220,8 +220,6 @@ module ActiveSupport # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes # insignificant zeros after the decimal separator (defaults to # +true+) - # * <tt>:prefix</tt> - If +:si+ formats the number using the SI - # prefix (defaults to :binary) # # ==== Examples # diff --git a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb index ac0d20b454..a4a8690bcd 100644 --- a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb @@ -7,6 +7,10 @@ module ActiveSupport self.validate_float = true def convert + if opts.key?(:prefix) + ActiveSupport::Deprecation.warn('The :prefix option of `number_to_human_size` is deprecated and will be removed in Rails 5.1 with no replacement.') + end + @number = Float(number) # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb index 83efbffdfb..cb5230c4eb 100644 --- a/activesupport/test/number_helper_test.rb +++ b/activesupport/test/number_helper_test.rb @@ -234,15 +234,17 @@ module ActiveSupport end def test_number_to_human_size_with_si_prefix - [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| - assert_equal '3 Bytes', number_helper.number_to_human_size(3.14159265, :prefix => :si) - assert_equal '123 Bytes', number_helper.number_to_human_size(123.0, :prefix => :si) - assert_equal '123 Bytes', number_helper.number_to_human_size(123, :prefix => :si) - assert_equal '1.23 KB', number_helper.number_to_human_size(1234, :prefix => :si) - assert_equal '12.3 KB', number_helper.number_to_human_size(12345, :prefix => :si) - assert_equal '1.23 MB', number_helper.number_to_human_size(1234567, :prefix => :si) - assert_equal '1.23 GB', number_helper.number_to_human_size(1234567890, :prefix => :si) - assert_equal '1.23 TB', number_helper.number_to_human_size(1234567890123, :prefix => :si) + assert_deprecated do + [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper| + assert_equal '3 Bytes', number_helper.number_to_human_size(3.14159265, :prefix => :si) + assert_equal '123 Bytes', number_helper.number_to_human_size(123.0, :prefix => :si) + assert_equal '123 Bytes', number_helper.number_to_human_size(123, :prefix => :si) + assert_equal '1.23 KB', number_helper.number_to_human_size(1234, :prefix => :si) + assert_equal '12.3 KB', number_helper.number_to_human_size(12345, :prefix => :si) + assert_equal '1.23 MB', number_helper.number_to_human_size(1234567, :prefix => :si) + assert_equal '1.23 GB', number_helper.number_to_human_size(1234567890, :prefix => :si) + assert_equal '1.23 TB', number_helper.number_to_human_size(1234567890123, :prefix => :si) + end end end diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 0f5a9e4e39..cd265331d6 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -260,7 +260,13 @@ $ bin/rake db:migrate == CreateHighScores: migrated (0.0019s) ====================================== ``` -INFO: Let's talk about unit tests. Unit tests are code that tests and makes assertions about code. In unit testing, we take a little part of code, say a method of a model, and test its inputs and outputs. Unit tests are your friend. The sooner you make peace with the fact that your quality of life will drastically increase when you unit test your code, the better. Seriously. We'll make one in a moment. +INFO: Let's talk about unit tests. Unit tests are code that tests and makes assertions +about code. In unit testing, we take a little part of code, say a method of a model, +and test its inputs and outputs. Unit tests are your friend. The sooner you make +peace with the fact that your quality of life will drastically increase when you unit +test your code, the better. Seriously. Please visit +[the testing guide](http://guides.rubyonrails.org/testing.html) for an in-depth +look at unit testing. Let's see the interface Rails created for us. diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 95dc3b01d7..ef66b75ec1 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -91,17 +91,17 @@ Open up a command line prompt. On Mac OS X open Terminal.app, on Windows choose dollar sign `$` should be run in the command line. Verify that you have a current version of Ruby installed: +```bash +$ ruby -v +ruby 2.2.2p95 +``` + TIP: A number of tools exist to help you quickly install Ruby and Ruby on Rails on your system. Windows users can use [Rails Installer](http://railsinstaller.org), while Mac OS X users can use [Tokaido](https://github.com/tokaido/tokaidoapp). For more installation methods for most Operating Systems take a look at [ruby-lang.org](https://www.ruby-lang.org/en/documentation/installation/). -```bash -$ ruby -v -ruby 2.2.2p95 -``` - Many popular UNIX-like OSes ship with an acceptable version of SQLite3. On Windows, if you installed Rails through Rails Installer, you already have SQLite installed. Others can find installation instructions diff --git a/guides/source/routing.md b/guides/source/routing.md index 732932b26e..e4799d93fa 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -83,8 +83,8 @@ Rails would dispatch that request to the `destroy` method on the `photos` contro ### CRUD, Verbs, and Actions -In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to -controller actions. By convention, each action also maps to a specific CRUD +In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to +controller actions. By convention, each action also maps to a specific CRUD operation in a database. A single entry in the routing file, such as: ```ruby @@ -1095,12 +1095,12 @@ edit_videos GET /videos/:identifier/edit(.:format) videos#edit Video.find_by(identifier: params[:identifier]) ``` -You can override `ActiveRecord::Base#to_param` of a related -model to constructe an URL. +You can override `ActiveRecord::Base#to_param` of a related model to construct +an URL: ```ruby class Video < ActiveRecord::Base - def to_param # overridden + def to_param identifier end end diff --git a/guides/source/security.md b/guides/source/security.md index 485b108d12..c701027479 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -93,9 +93,16 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves * Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _Storing the current user's database id in a session is usually ok_. -* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie. +* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret (`secrets.secret_token`) and inserted into the end of the cookie. -That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA1, for compatibility). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. +However, since Rails 4, the default store is EncryptedCookieStore. With +EncryptedCookieStore the session is encrypted before being stored in a cookie. +This prevents the user from accessing and tampering the content of the cookie. +Thus the session becomes a more secure place to store data. The encryption is +done using a server-side secret key `secrets.secret_key_base` stored in +`config/secrets.yml`. + +That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA1, for compatibility). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters, use `rake secret` instead_. `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`, e.g.: diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 4c996dd2d0..1c42ff2914 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -357,7 +357,7 @@ This gem uses Ajax to speed up page rendering in most applications. Turbolinks attaches a click handler to all `<a>` on the page. If your browser supports -[PushState](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#The_pushState()_method), +[PushState](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#The_pushState%28%29_method), Turbolinks will make an Ajax request for the page, parse the response, and replace the entire `<body>` of the page with the `<body>` of the response. It will then use PushState to change the URL to the correct one, preserving |