diff options
38 files changed, 320 insertions, 681 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 236768227c..880263ce87 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,35 @@ ## Rails 4.0.0 (unreleased) ## +* Add 'X-Frame-Options' => 'SAMEORIGIN' and + 'X-XSS-Protection' => '1; mode=block' + as default headers. + + *Egor Homakov* + +* Allow data attributes to be set as a first-level option for form_for, so you can write `form_for @record, data: { behavior: 'autosave' }` instead of `form_for @record, html: { data: { behavior: 'autosave' } }` *DHH* + +* Deprecate `button_to_function` and `link_to_function` helpers. + + We recommend the use of Unobtrusive JavaScript instead. For example: + + link_to "Greeting", "#", :class => "nav_link" + + $(function() { + $('.nav_link').click(function() { + // Some complex code + + return false; + }); + }); + + or + + link_to "Greeting", '#', onclick: "alert('Hello world!'); return false", class: "nav_link" + + for simple cases. + + *Rafael Mendonça França* + * `javascript_include_tag :all` will now not include `application.js` if the file does not exists. *Prem Sichanugrist* * Send an empty response body when call `head` with status between 100 and 199, 204, 205 or 304. @@ -146,8 +176,6 @@ * Replace `include_seconds` boolean argument with `:include_seconds => true` option in `distance_of_time_in_words` and `time_ago_in_words` signature. *Dmitriy Kiriyenko* -* Remove `button_to_function` and `link_to_function` helpers. *Rafael Mendonça França* - * Make current object and counter (when it applies) variables accessible when rendering templates with :object / :collection. *Carlos Antonio da Silva* diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc index 1fdc57e14d..ccd0193515 100644 --- a/actionpack/README.rdoc +++ b/actionpack/README.rdoc @@ -28,291 +28,6 @@ by default and Action View rendering is implicitly triggered by Action Controller. However, these modules are designed to function on their own and can be used outside of Rails. -A short rundown of some of the major features: - -* Actions grouped in controller as methods instead of separate command objects - and can therefore share helper methods - - class CustomersController < ActionController::Base - def show - @customer = find_customer - end - - def update - @customer = find_customer - if @customer.update_attributes(params[:customer]) - redirect_to :action => "show" - else - render :action => "edit" - end - end - - private - def find_customer - Customer.find params[:id] - end - end - - {Learn more}[link:classes/ActionController/Base.html] - - -* ERB templates (static content mixed with dynamic output from ruby) - - <% @posts.each do |post| %> - Title: <%= post.title %> - <% end %> - - All post titles: <%= @posts.collect{ |p| p.title }.join(", ") %> - - <% unless @person.is_client? %> - Not for clients to see... - <% end %> - - {Learn more}[link:classes/ActionView.html] - - -* "Builder" templates (great for XML content, like RSS) - - xml.rss("version" => "2.0") do - xml.channel do - xml.title(@feed_title) - xml.link(@url) - xml.description "Basecamp: Recent items" - xml.language "en-us" - xml.ttl "40" - - @recent_items.each do |item| - xml.item do - xml.title(item_title(item)) - xml.description(item_description(item)) - xml.pubDate(item_pubDate(item)) - xml.guid(@recent_items.url(item)) - xml.link(@recent_items.url(item)) - end - end - end - end - - {Learn more}[link:classes/ActionView/Base.html] - - -* Filters for pre- and post-processing of the response - - class WeblogController < ActionController::Base - # filters as methods - before_filter :authenticate, :cache, :audit - - # filter as a proc - after_filter { |c| c.response.body = Gzip::compress(c.response.body) } - - # class filter - after_filter LocalizeFilter - - def index - # Before this action is run, the user will be authenticated, the cache - # will be examined to see if a valid copy of the results already - # exists, and the action will be logged for auditing. - - # After this action has run, the output will first be localized then - # compressed to minimize bandwidth usage - end - - private - def authenticate - # Implement the filter with full access to both request and response - end - end - - {Learn more}[link:classes/ActionController/Filters/ClassMethods.html] - - -* Helpers for forms, dates, action links, and text - - <%= text_field_tag "post", "title", "size" => 30 %> - <%= link_to "New post", :controller => "post", :action => "new" %> - <%= truncate(post.title, :length => 25) %> - - {Learn more}[link:classes/ActionView/Helpers.html] - - -* Layout sharing for template reuse - - class WeblogController < ActionController::Base - layout "weblog_layout" - - def hello_world - end - end - - Layout file (called weblog_layout): - <html><body><%= yield %></body></html> - - Template for hello_world action: - <h1>Hello world</h1> - - Result of running hello_world action: - <html><body><h1>Hello world</h1></body></html> - - {Learn more}[link:classes/ActionController/Layout/ClassMethods.html] - - -* Routing makes pretty URLs incredibly easy - - match 'clients/:client_name/:project_name/:controller/:action' - - Accessing "/clients/37signals/basecamp/project/index" calls ProjectController#index with - { "client_name" => "37signals", "project_name" => "basecamp" } in `params` - - From that action, you can write the redirect in a number of ways: - - redirect_to(:action => "edit") => - /clients/37signals/basecamp/project/edit - - redirect_to(:client_name => "nextangle", :project_name => "rails") => - /clients/nextangle/rails/project/index - - {Learn more}[link:classes/ActionDispatch/Routing.html] - - -* Easy testing of both controller and rendered template through ActionController::TestCase - - class LoginControllerTest < ActionController::TestCase - def test_failing_authenticate - process :authenticate, :user_name => "nop", :password => "" - assert flash.has_key?(:alert) - assert_redirected_to :action => "index" - end - end - - {Learn more}[link:classes/ActionController/TestCase.html] - - -* Automated benchmarking and integrated logging - - Started GET "/weblog" for 127.0.0.1 at Fri May 28 00:41:55 - Processing by WeblogController#index as HTML - Rendered weblog/index.html.erb within layouts/application (25.7ms) - Completed 200 OK in 29.3ms - - If Active Record is used as the model, you'll have the database debugging - as well: - - Started POST "/posts" for 127.0.0.1 at Sat Jun 19 14:04:23 - Processing by PostsController#create as HTML - Parameters: {"post"=>{"title"=>"this is good"}} - SQL (0.6ms) INSERT INTO posts (title) VALUES('this is good') - Redirected to http://example.com/posts/5 - Completed 302 Found in 221ms (Views: 215ms | ActiveRecord: 0.6ms) - - You specify a logger through a class method, such as: - - ActionController::Base.logger = ActiveSupport::Logger.new("Application Log") - ActionController::Base.logger = Log4r::Logger.new("Application Log") - - -* Caching at three levels of granularity (page, action, fragment) - - class WeblogController < ActionController::Base - caches_page :show - caches_action :account - - def show - # the output of the method will be cached as - # ActionController::Base.page_cache_directory + "/weblog/show.html" - # and the web server will pick it up without even hitting Rails - end - - def account - # the output of the method will be cached in the fragment store - # but Rails is hit to retrieve it, so filters are run - end - - def update - List.update(params[:list][:id], params[:list]) - expire_page :action => "show", :id => params[:list][:id] - expire_action :action => "account" - redirect_to :action => "show", :id => params[:list][:id] - end - end - - {Learn more}[link:classes/ActionController/Caching.html] - - -* Powerful debugging mechanism for local requests - - All exceptions raised on actions performed on the request of a local user - will be presented with a tailored debugging screen that includes exception - message, stack trace, request parameters, session contents, and the - half-finished response. - - {Learn more}[link:classes/ActionController/Rescue.html] - - -== Simple example (from outside of Rails) - -This example will implement a simple weblog system using inline templates and -an Active Record model. So let's build that WeblogController with just a few -methods: - - require 'action_controller' - require 'post' - - class WeblogController < ActionController::Base - layout "weblog/layout" - - def index - @posts = Post.all - end - - def show - @post = Post.find(params[:id]) - end - - def new - @post = Post.new - end - - def create - @post = Post.create(params[:post]) - redirect_to :action => "show", :id => @post.id - end - end - - WeblogController::Base.view_paths = [ File.dirname(__FILE__) ] - WeblogController.process_cgi if $0 == __FILE__ - -The last two lines are responsible for telling ActionController where the -template files are located and actually running the controller on a new -request from the web-server (e.g., Apache). - -And the templates look like this: - - weblog/layout.html.erb: - <html><body> - <%= yield %> - </body></html> - - weblog/index.html.erb: - <% @posts.each do |post| %> - <p><%= link_to(post.title, :action => "show", :id => post.id) %></p> - <% end %> - - weblog/show.html.erb: - <p> - <b><%= @post.title %></b><br/> - <b><%= @post.content %></b> - </p> - - weblog/new.html.erb: - <%= form "post" %> - -This simple setup will list all the posts in the system on the index page, -which is called by accessing /weblog/. It uses the form builder for the Active -Record model to make the new screen, which in turn hands everything over to -the create action (that's the default target for the form builder when given a -new model). After creating the post, it'll redirect to the show page using -an URL such as /weblog/5 (where 5 is the id of the post). - == Download and installation diff --git a/actionpack/examples/performance.rb b/actionpack/examples/performance.rb deleted file mode 100644 index 8ea4758961..0000000000 --- a/actionpack/examples/performance.rb +++ /dev/null @@ -1,185 +0,0 @@ -ENV['RAILS_ENV'] ||= 'production' - -require File.expand_path('../../../load_paths', __FILE__) -require 'action_pack' -require 'action_controller' -require 'action_view' -require 'active_model' -require 'benchmark' - -MyHash = Class.new(Hash) - -Hash.class_eval do - extend ActiveModel::Naming - include ActiveModel::Conversion -end - -class Runner - def initialize(app, output) - @app, @output = app, output - end - - def puts(*) - super if @output - end - - def call(env) - env['n'].to_i.times { @app.call(env) } - @app.call(env).tap { |response| report(env, response) } - end - - def report(env, response) - return unless ENV["DEBUG"] - out = env['rack.errors'] - out.puts response[0], response[1].to_yaml, '---' - response[2].each { |part| out.puts part } - out.puts '---' - end - - def self.puts(*) - super if @output - end - - def self.print(*) - super if @output - end - - def self.app_and_env_for(action, n) - env = Rack::MockRequest.env_for("/") - env.merge!('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) - app = lambda { |env| BasePostController.action(action).call(env) } - return app, env - end - - $ran = [] - - def self.run(action, n, output = true) - print "." - STDOUT.flush - @output = output - label = action.to_s - app, env = app_and_env_for(action, n) - t = Benchmark.realtime { new(app, output).call(env) } - $ran << [label, (t * 1000).to_i.to_s] if output - end - - def self.done - puts - header, content = "", "" - $ran.each do |k,v| - size = [k.size, v.size].max + 1 - header << format("%#{size}s", k) - content << format("%#{size}s", v) - end - puts header - puts content - end -end - -ActionController::Base.logger = nil -ActionController::Base.config.compile_methods! -ActionView::Resolver.caching = ENV["RAILS_ENV"] == "production" - -class BasePostController < ActionController::Base - append_view_path "#{File.dirname(__FILE__)}/views" - - def overhead - self.response_body = '' - end - - def index - render :text => '' - end - - $OBJECT = {:name => "Hello my name is omg", :address => "333 omg"} - - def partial - render :partial => "/collection", :object => $OBJECT - end - - def partial_10 - render :partial => "/ten_partials" - end - - def partial_100 - render :partial => "/hundred_partials" - end - - $COLLECTION1 = [] - 10.times do |i| - $COLLECTION1 << { :name => "Hello my name is omg", :address => "333 omg" } - end - - def coll_10 - render :partial => "/collection", :collection => $COLLECTION1 - end - - $COLLECTION2 = [] - 100.times do |i| - $COLLECTION2 << { :name => "Hello my name is omg", :address => "333 omg" } - end - - def coll_100 - render :partial => "/collection", :collection => $COLLECTION2 - end - - def uniq_100 - render :partial => $COLLECTION2 - end - - $COLLECTION3 = [] - 50.times do |i| - $COLLECTION3 << {:name => "Hello my name is omg", :address => "333 omg"} - $COLLECTION3 << MyHash.new(:name => "Hello my name is omg", :address => "333 omg") - end - - def diff_100 - render :partial => $COLLECTION3 - end - - def template_1 - render :template => "template" - end - - module Foo - def omg - "omg" - end - end - - helper Foo -end - -N = (ENV['N'] || 1000).to_i -# ActionController::Base.use_accept_header = false - -def run_all!(times, verbose) - Runner.run(:overhead, times, verbose) - Runner.run(:index, times, verbose) - Runner.run(:template_1, times, verbose) - Runner.run(:partial, times, verbose) - Runner.run(:partial_10, times, verbose) - Runner.run(:coll_10, times, verbose) - Runner.run(:partial_100, times, verbose) - Runner.run(:coll_100, times, verbose) - Runner.run(:uniq_100, times, verbose) - Runner.run(:diff_100, times, verbose) -end - -if ENV["PROFILE"] - Runner.run(ENV["PROFILE"].to_sym, 1, false) - require "ruby-prof" - RubyProf.start - Runner.run(ENV["PROFILE"].to_sym, N, true) - result = RubyProf.stop - printer = RubyProf::CallStackPrinter.new(result) - printer.print(File.open("output.html", "w")) -else - run_all!(1, false) - - (ENV["M"] || 1).to_i.times do - $ran = [] - run_all!(N, true) - Runner.done - end -end
\ No newline at end of file diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb deleted file mode 100644 index cee3fe64c0..0000000000 --- a/actionpack/examples/views/_collection.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= collection[:name] %> -<%= collection[:address] %> -<%= omg %>
\ No newline at end of file diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb deleted file mode 100644 index 5ab2f8a432..0000000000 --- a/actionpack/examples/views/_hello.erb +++ /dev/null @@ -1 +0,0 @@ -Hello
\ No newline at end of file diff --git a/actionpack/examples/views/_hundred_partials.erb b/actionpack/examples/views/_hundred_partials.erb deleted file mode 100644 index 35c2a6c9d3..0000000000 --- a/actionpack/examples/views/_hundred_partials.erb +++ /dev/null @@ -1,3 +0,0 @@ -<% 100.times do %> - <%= render :partial => "/collection", :object => $OBJECT %> -<% end %>
\ No newline at end of file diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb deleted file mode 100644 index 3ca8e80b52..0000000000 --- a/actionpack/examples/views/_partial.erb +++ /dev/null @@ -1,10 +0,0 @@ -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> diff --git a/actionpack/examples/views/_ten_partials.erb b/actionpack/examples/views/_ten_partials.erb deleted file mode 100644 index fd02991e22..0000000000 --- a/actionpack/examples/views/_ten_partials.erb +++ /dev/null @@ -1,10 +0,0 @@ -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %> -<%= render :partial => '/collection', :object => $OBJECT %>
\ No newline at end of file diff --git a/actionpack/examples/views/hashes/_hash.erb b/actionpack/examples/views/hashes/_hash.erb deleted file mode 100644 index c100a290bd..0000000000 --- a/actionpack/examples/views/hashes/_hash.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= hash[:name] %> -<%= hash[:address] %> -<%= omg %>
\ No newline at end of file diff --git a/actionpack/examples/views/my_hashes/_my_hash.erb b/actionpack/examples/views/my_hashes/_my_hash.erb deleted file mode 100644 index e25d84101a..0000000000 --- a/actionpack/examples/views/my_hashes/_my_hash.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= my_hash[:name] %> -<%= my_hash[:address] %> -<%= omg %>
\ No newline at end of file diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb deleted file mode 100644 index 5ab2f8a432..0000000000 --- a/actionpack/examples/views/template.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello
\ No newline at end of file diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index abeb49d16f..9c77b0ccf4 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -5,48 +5,18 @@ module ActionController #:nodoc: # useful when certain elements of an action change frequently or # depend on complicated state while other parts rarely change or # can be shared amongst multiple parties. The caching is done using - # the <tt>cache</tt> helper available in the Action View. A - # template with fragment caching might look like: + # the <tt>cache</tt> helper available in the Action View. See + # ActionView::Helpers::CacheHelper for more information. # - # <b>Hello <%= @name %></b> + # While it's strongly recommended that you use key-based cache + # expiration (see links in CacheHelper for more information), + # it is also possible to manually expire caches. For example: # - # <% cache do %> - # All the topics in the system: - # <%= render :partial => "topic", :collection => Topic.all %> - # <% end %> - # - # This cache will bind the name of the action that called it, so if - # this code was part of the view for the topics/list action, you - # would be able to invalidate it using: - # - # expire_fragment(:controller => "topics", :action => "list") - # - # This default behavior is limited if you need to cache multiple - # fragments per action or if the action itself is cached using - # <tt>caches_action</tt>. To remedy this, there is an option to - # qualify the name of the cached fragment by using the - # <tt>:action_suffix</tt> option: - # - # <% cache(:action => "list", :action_suffix => "all_topics") do %> - # - # That would result in a name such as - # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the - # action cache and with any fragments that use a different suffix. - # Note that the URL doesn't have to really exist or be callable - # - the url_for system is just used to generate unique cache names - # that we can refer to when we need to expire the cache. - # - # The expiration call for this example is: - # - # expire_fragment(:controller => "topics", - # :action => "list", - # :action_suffix => "all_topics") + # expire_fragment("name_of_cache") module Fragments # Given a key (as described in <tt>expire_fragment</tt>), returns # a key suitable for use in reading, writing, or expiring a - # cached fragment. If the key is a hash, the generated key is the - # return value of url_for on that hash (without the protocol). - # All keys are prefixed with <tt>views/</tt> and uses + # cached fragment. All keys are prefixed with <tt>views/</tt> and uses # ActiveSupport::Cache.expand_cache_key for the expansion. def fragment_cache_key(key) ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views) diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index 43a9e3aa9d..d8fe43b5af 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -33,13 +33,13 @@ module ActionController module Live class Buffer < ActionDispatch::Response::Buffer #:nodoc: def initialize(response) - super(response, Queue.new) + super(response, SizedQueue.new(10)) end def write(string) unless @response.committed? @response.headers["Cache-Control"] = "no-cache" - @response.headers.delete("Content-Length") + @response.headers.delete "Content-Length" end super @@ -47,13 +47,13 @@ module ActionController def each while str = @buf.pop - yield(str) + yield str end end def close super - @buf.push(nil) + @buf.push nil end end @@ -78,7 +78,7 @@ module ActionController end def initialize(status = 200, header = {}, body = []) - header = Header.new(self, header) + header = Header.new self, header super(status, header, body) end @@ -89,11 +89,11 @@ module ActionController private - def build_buffer(response, body) - buf = Live::Buffer.new(response) - body.each { |part| buf.write(part) } - buf - end + def build_buffer(response, body) + buf = Live::Buffer.new response + body.each { |part| buf.write part } + buf + end end def process(name) diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index bcfd0b0d00..9a7b5bc8c7 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -4,6 +4,11 @@ require 'active_support/core_ext/hash/indifferent_access' module ActionDispatch module Http module Parameters + def initialize(env) + super + @symbolized_path_params = nil + end + # Returns both GET and POST \parameters in a single hash. def parameters @env["action_dispatch.request.parameters"] ||= begin diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 1377e53ce8..d24c7c7f3f 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -38,6 +38,17 @@ module ActionDispatch METHOD end + def initialize(env) + super + @method = nil + @request_method = nil + @remote_ip = nil + @original_fullpath = nil + @fullpath = nil + @ip = nil + @uuid = nil + end + def key?(key) @env.key?(key) end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index d336808e7c..5014ad80aa 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -58,6 +58,7 @@ module ActionDispatch # :nodoc: LOCATION = "Location".freeze cattr_accessor(:default_charset) { "utf-8" } + cattr_accessor(:default_headers) include Rack::Response::Helpers include ActionDispatch::Http::Cache::Response @@ -96,6 +97,10 @@ module ActionDispatch # :nodoc: def initialize(status = 200, header = {}, body = []) super() + if self.class.default_headers.respond_to?(:merge) + header = self.class.default_headers.merge(header) + end + self.body, self.header, self.status = body, header, status @sending_file = false diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 4266ec042e..8aa02ec482 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -87,6 +87,12 @@ module ActionDispatch end end + def initialize(env) + super + @protocol = nil + @port = nil + end + # Returns the complete URL used for this request. def url protocol + host_with_port + fullpath diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 62f906219c..e7f3f07390 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -23,6 +23,7 @@ module ActionDispatch ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding + ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses) ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates) diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index 33799d7d71..39518268df 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -8,28 +8,28 @@ module ActionView # fragments, and so on. This method takes a block that contains # the content you wish to cache. # - # See ActionController::Caching::Fragments for usage instructions. + # The best way to use this is by doing key-based cache expiration + # on top of a cache store like Memcached that'll automatically + # kick out old entries. For more on key-based expiration, see: + # http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works # - # If you want to cache a navigation menu, you can do following: + # When using this method, you list the cache dependencies as part of + # the name of the cache, like so: # - # <% cache do %> - # <%= render :partial => "menu" %> + # <% cache [ "v1", project ] do %> + # <b>All the topics on this project</b> + # <%= render project.topics %> # <% end %> # - # You can also cache static content: + # This approach will assume that when a new topic is added, you'll touch + # the project. The cache key generated from this call will be something like: # - # <% cache do %> - # <p>Hello users! Welcome to our website!</p> - # <% end %> - # - # Static content with embedded ruby content can be cached as - # well: + # views/v1/projects/123-20120806214154 + # ^class ^id ^updated_at # - # <% cache do %> - # Topics: - # <%= render :partial => "topics", :collection => @topic_list %> - # <i>Topics listed alphabetically</i> - # <% end %> + # If you update the rendering of topics, you just bump the version to v2. + # Otherwise the cache is automatically bumped whenever the project updated_at + # is touched. def cache(name = {}, options = nil, &block) if controller.perform_caching safe_concat(fragment_for(name, options, &block)) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 13a5671a17..5cfcfdd8d5 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -323,6 +323,24 @@ module ActionView # ... # </form> # + # === Setting HTML options + # + # You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in + # the HTML key. Example: + # + # <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %> + # ... + # <% end %> + # + # The HTML generated for this would be: + # + # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'> + # <div style='margin:0;padding:0;display:inline'> + # <input name='_method' type='hidden' value='put' /> + # </div> + # ... + # </form> + # # === Removing hidden model id's # # The form_for method automatically includes the model id as a hidden field in the form. @@ -409,6 +427,7 @@ module ActionView apply_form_for_options!(record, object, options) end + options[:html][:data] = options.delete(:data) if options.has_key?(:data) options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote) options[:html][:method] = options.delete(:method) if options.has_key?(:method) options[:html][:authenticity_token] = options.delete(:authenticity_token) diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index cc20518b93..9f8cd8caaa 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -67,6 +67,46 @@ module ActionView def javascript_cdata_section(content) #:nodoc: "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe end + + # Returns a button whose +onclick+ handler triggers the passed JavaScript. + # + # The helper receives a name, JavaScript code, and an optional hash of HTML options. The + # name is used as button label and the JavaScript code goes into its +onclick+ attribute. + # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. + # + # button_to_function "Greeting", "alert('Hello world!')", :class => "ok" + # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" /> + # + def button_to_function(name, function=nil, html_options={}) + message = "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." + ActiveSupport::Deprecation.warn message + + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" + + tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) + end + + # Returns a link whose +onclick+ handler triggers the passed JavaScript. + # + # The helper receives a name, JavaScript code, and an optional hash of HTML options. The + # name is used as the link text and the JavaScript code goes into the +onclick+ attribute. + # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all + # the JavaScript is set, the helper appends "; return false;". + # + # The +href+ attribute of the tag is set to "#" unless +html_options+ has one. + # + # link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link" + # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a> + # + def link_to_function(name, function, html_options={}) + message = "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." + ActiveSupport::Deprecation.warn message + + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" + href = html_options[:href] || '#' + + content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) + end end end end diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index a727b910e5..aaf0e0344a 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -78,7 +78,7 @@ module ActionView # strip_tags("<div id='top-bar'>Welcome to my website!</div>") # # => Welcome to my website! def strip_tags(html) - self.class.full_sanitizer.sanitize(html).try(:html_safe) + self.class.full_sanitizer.sanitize(html) end # Strips all link tags from +text+ leaving just the link text. diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 56cebee678..b914bbce4d 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -85,39 +85,28 @@ module RenderERBUtils end end -module SetupOnce - extend ActiveSupport::Concern - - included do - cattr_accessor :setup_once_block - self.setup_once_block = nil - - setup :run_setup_once - end +SharedTestRoutes = ActionDispatch::Routing::RouteSet.new - module ClassMethods - def setup_once(&block) - self.setup_once_block = block +module ActionDispatch + module SharedRoutes + def before_setup + @routes = SharedTestRoutes + super end end - private - def run_setup_once - if self.setup_once_block - self.setup_once_block.call - self.setup_once_block = nil - end + # Hold off drawing routes until all the possible controller classes + # have been loaded. + module DrawOnce + class << self + attr_accessor :drew end -end + self.drew = false -SharedTestRoutes = ActionDispatch::Routing::RouteSet.new + def before_setup + super + return if DrawOnce.drew -module ActiveSupport - class TestCase - include SetupOnce - # Hold off drawing routes until all the possible controller classes - # have been loaded. - setup_once do SharedTestRoutes.draw do get ':controller(/:action)' end @@ -125,10 +114,18 @@ module ActiveSupport ActionDispatch::IntegrationTest.app.routes.draw do get ':controller(/:action)' end + + DrawOnce.drew = true end end end +module ActiveSupport + class TestCase + include ActionDispatch::DrawOnce + end +end + class RoutedRackApp attr_reader :routes @@ -159,9 +156,7 @@ class BasicController end class ActionDispatch::IntegrationTest < ActiveSupport::TestCase - setup do - @routes = SharedTestRoutes - end + include ActionDispatch::SharedRoutes def self.build_app(routes = nil) RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware| @@ -290,10 +285,7 @@ module ActionController class TestCase include ActionDispatch::TestProcess - - setup do - @routes = SharedTestRoutes - end + include ActionDispatch::SharedRoutes end end @@ -304,9 +296,7 @@ module ActionView class TestCase # Must repeat the setup because AV::TestCase is a duplication # of AC::TestCase - setup do - @routes = SharedTestRoutes - end + include ActionDispatch::SharedRoutes end end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index d5afef9086..0efba5b77f 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -349,15 +349,18 @@ class ActionCachingMockController end class ActionCacheTest < ActionController::TestCase + tests ActionCachingTestController + def setup super - reset! + @request.host = 'hostname.com' FileUtils.mkdir_p(FILE_STORE_PATH) @path_class = ActionController::Caching::Actions::ActionCachePath @mock_controller = ActionCachingMockController.new end def teardown + super FileUtils.rm_rf(File.dirname(FILE_STORE_PATH)) end @@ -367,7 +370,6 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?('hostname.com/action_caching_test') - reset! get :index assert_response :success @@ -380,7 +382,6 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_equal cached_time, @response.body assert !fragment_exist?('hostname.com/action_caching_test/destroy') - reset! get :destroy assert_response :success @@ -395,7 +396,6 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_not_equal cached_time, @response.body assert fragment_exist?('hostname.com/action_caching_test/with_layout') - reset! get :with_layout assert_response :success @@ -410,7 +410,6 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_not_equal cached_time, @response.body assert fragment_exist?('hostname.com/action_caching_test/layout_false') - reset! get :layout_false assert_response :success @@ -425,7 +424,6 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_not_equal cached_time, @response.body assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param') - reset! get :with_layout_proc_param, :layout => false assert_response :success @@ -440,7 +438,6 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_not_equal cached_time, @response.body assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param') - reset! get :with_layout_proc_param, :layout => true assert_response :success @@ -477,7 +474,6 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?('test.host/custom/show') - reset! get :show assert_response :success @@ -488,7 +484,6 @@ class ActionCacheTest < ActionController::TestCase get :edit assert_response :success assert fragment_exist?('test.host/edit') - reset! get :edit, :id => 1 assert_response :success @@ -499,22 +494,18 @@ class ActionCacheTest < ActionController::TestCase get :index assert_response :success cached_time = content_to_cache - reset! get :index assert_response :success assert_equal cached_time, @response.body - reset! get :expire assert_response :success - reset! get :index assert_response :success new_cached_time = content_to_cache assert_not_equal cached_time, @response.body - reset! get :index assert_response :success @@ -524,12 +515,10 @@ class ActionCacheTest < ActionController::TestCase def test_cache_expiration_isnt_affected_by_request_format get :index cached_time = content_to_cache - reset! @request.request_uri = "/action_caching_test/expire.xml" get :expire, :format => :xml assert_response :success - reset! get :index assert_response :success @@ -539,12 +528,10 @@ class ActionCacheTest < ActionController::TestCase def test_cache_expiration_with_url_string get :index cached_time = content_to_cache - reset! @request.request_uri = "/action_caching_test/expire_with_url_string" get :expire_with_url_string assert_response :success - reset! get :index assert_response :success @@ -557,23 +544,17 @@ class ActionCacheTest < ActionController::TestCase assert_response :success jamis_cache = content_to_cache - reset! - @request.host = 'david.hostname.com' get :index assert_response :success david_cache = content_to_cache assert_not_equal jamis_cache, @response.body - reset! - @request.host = 'jamis.hostname.com' get :index assert_response :success assert_equal jamis_cache, @response.body - reset! - @request.host = 'david.hostname.com' get :index assert_response :success @@ -583,8 +564,6 @@ class ActionCacheTest < ActionController::TestCase def test_redirect_is_not_cached get :redirected assert_response :redirect - reset! - get :redirected assert_response :redirect end @@ -592,8 +571,6 @@ class ActionCacheTest < ActionController::TestCase def test_forbidden_is_not_cached get :forbidden assert_response :forbidden - reset! - get :forbidden assert_response :forbidden end @@ -609,17 +586,14 @@ class ActionCacheTest < ActionController::TestCase cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?('hostname.com/action_caching_test/index.xml') - reset! get :index, :format => 'xml' assert_response :success assert_equal cached_time, @response.body assert_equal 'application/xml', @response.content_type - reset! get :expire_xml assert_response :success - reset! get :index, :format => 'xml' assert_response :success @@ -724,14 +698,6 @@ class ActionCacheTest < ActionController::TestCase assigns(:cache_this) end - def reset! - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - @controller = ActionCachingTestController.new - @controller.singleton_class.send(:include, @routes.url_helpers) - @request.host = 'hostname.com' - end - def fragment_exist?(path) @controller.fragment_exist?(path) end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 2467654a70..347b3b3b5a 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -190,7 +190,7 @@ class CookiesTest < ActionController::TestCase def test_setting_the_same_value_to_permanent_cookie request.cookies[:user_name] = 'Jamie' get :set_permanent_cookie - assert response.cookies, 'user_name' => 'Jamie' + assert_equal response.cookies, 'user_name' => 'Jamie' end def test_setting_with_escapable_characters diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index e2903d4b36..71609d7340 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -176,6 +176,33 @@ class ResponseTest < ActiveSupport::TestCase ActionDispatch::Response.default_charset = original end end + + test "read x_frame_options and x_xss_protection" do + ActionDispatch::Response.default_headers = { + 'X-Frame-Options' => 'DENY', + 'X-XSS-Protection' => '1;' + } + resp = ActionDispatch::Response.new.tap { |response| + response.body = 'Hello' + } + resp.to_a + + assert_equal('DENY', resp.headers['X-Frame-Options']) + assert_equal('1;', resp.headers['X-XSS-Protection']) + end + + test "read custom default_header" do + ActionDispatch::Response.default_headers = { + 'X-XX-XXXX' => 'Here is my phone number' + } + resp = ActionDispatch::Response.new.tap { |response| + response.body = 'Hello' + } + resp.to_a + + assert_equal('Here is my phone number', resp.headers['X-XX-XXXX']) + end + end class ResponseIntegrationTest < ActionDispatch::IntegrationTest diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index a20a68c1b2..152b35ff0f 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -2637,6 +2637,12 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_form_for_with_data_attributes + form_for(@post, data: { behavior: "stuff" }, remote: true) {} + assert_match %r|data-behavior="stuff"|, output_buffer + assert_match %r|data-remote="true"|, output_buffer + end + def test_fields_for_returns_block_result output = fields_for(Post.new) { |f| "fields" } assert_equal "fields", output diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index fe7607ee26..4a9a382afa 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -42,6 +42,48 @@ class JavaScriptHelperTest < ActionView::TestCase assert_instance_of ActiveSupport::SafeBuffer, escape_javascript(ActiveSupport::SafeBuffer.new(given)) end + def test_button_to_function + assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />), + button_to_function("Greeting", "alert('Hello world!')") + end + end + + def test_button_to_function_with_onclick + assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />", + button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')") + end + end + + def test_button_to_function_without_function + assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />", + button_to_function("Greeting") + end + end + + def test_link_to_function + assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>), + link_to_function("Greeting", "alert('Hello world!')") + end + end + + def test_link_to_function_with_existing_onclick + assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>), + link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')") + end + end + + def test_function_with_href + assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do + assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>), + link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/') + end + end + def test_javascript_tag self.output_buffer = 'foo' diff --git a/actionpack/test/template/sanitize_helper_test.rb b/actionpack/test/template/sanitize_helper_test.rb index 4182af590e..7626cdf386 100644 --- a/actionpack/test/template/sanitize_helper_test.rb +++ b/actionpack/test/template/sanitize_helper_test.rb @@ -40,9 +40,9 @@ class SanitizeHelperTest < ActionView::TestCase [nil, '', ' '].each do |blank| stripped = strip_tags(blank) assert_equal blank, stripped - assert stripped.html_safe? unless blank.nil? end - assert strip_tags("<script>").html_safe? + assert_equal "", strip_tags("<script>") + assert_equal "something <img onerror=alert(1337)", ERB::Util.html_escape(strip_tags("something <img onerror=alert(1337)")) end def test_sanitize_is_marked_safe diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 7ba439fb3e..2102282c02 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -300,11 +300,9 @@ module ActiveModel private def self.model_name_from_record_or_class(record_or_class) - (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name - end - - def self.convert_to_model(object) - object.respond_to?(:to_model) ? object.to_model : object + return record_or_class.model_name if record_or_class.respond_to?(:model_name) + return record_or_class.to_model.class.model_name if record_or_class.respond_to?(:to_model) + record_or_class.class.model_name end end diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 202f7e27c5..1ff307c735 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -85,16 +85,17 @@ module ActiveRecord assert @connection.active? original_connection_pid = @connection.query('select pg_backend_pid()') - # Fail with bad connection after next query attempt. - connection_class = class << @connection ; self ; end - connection_class.class_eval <<-CODE + # Fail with bad connection on next query attempt. + raw_connection = @connection.raw_connection + raw_connection_class = class << raw_connection ; self ; end + raw_connection_class.class_eval <<-CODE, __FILE__, __LINE__ + 1 def query_fake(*args) - if @called ||= false - @connection.stubs(:status).returns(PCconn::CONNECTION_BAD) + if !( @called ||= false ) + self.stubs(:status).returns(PGconn::CONNECTION_BAD) + @called = true raise PGError else - @called = true - @connection.unstub(:status) + self.unstub(:status) query_unfake(*args) end end @@ -107,13 +108,13 @@ module ActiveRecord @connection.verify! new_connection_pid = @connection.query('select pg_backend_pid()') ensure - connection_class.class_eval <<-CODE + raw_connection_class.class_eval <<-CODE alias query query_unfake undef query_fake CODE end - assert_equal original_connection_pid, new_connection_pid, "Should have a new underlying connection pid" + assert_not_equal original_connection_pid, new_connection_pid, "Should have a new underlying connection pid" end # Must have with_manual_interventions set to true for this diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 70bd1bf4d3..6229b89a91 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,10 +1,6 @@ ## Rails 4.0.0 (unreleased) ## -* Add String#indent. *fxn & Ace Suares* - -* Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument. *David Celis* - -* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!` *DHH* +* `ERB::Util.html_escape` now escapes single quotes. *Santiago Pastorino* * `Time#change` now works with time values with offsets other than UTC or the local time zone. *Andrew White* diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 389df58ec4..1a95bd63e6 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/object/to_json' require 'active_support/core_ext/module/delegation' +require 'active_support/json/variable' require 'bigdecimal' require 'active_support/core_ext/big_decimal/conversions' # for #to_s diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb new file mode 100644 index 0000000000..8af661a795 --- /dev/null +++ b/activesupport/lib/active_support/json/variable.rb @@ -0,0 +1,17 @@ +require 'active_support/deprecation' + +module ActiveSupport + module JSON + # Deprecated: A string that returns itself as its JSON-encoded form. + class Variable < String + def initialize(*args) + ActiveSupport::Deprecation.warn 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \ + 'For your own custom JSON literals, define #as_json and #encode_json yourself.' + super + end + + def as_json(options = nil) self end #:nodoc: + def encode_json(encoder) self end #:nodoc: + end + end +end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index a947635f4a..7ed71f9abc 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -85,6 +85,13 @@ class TestJSONEncoding < ActiveSupport::TestCase end end + def test_json_variable + assert_deprecated do + assert_equal ActiveSupport::JSON::Variable.new('foo'), 'foo' + assert_equal ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")' + end + end + def test_hash_encoding assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b) assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1) diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 5915d20010..f20dd78031 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -27,9 +27,6 @@ module <%= app_const_base %> # Custom directories with classes and modules you want to be autoloadable. # config.autoload_paths += %W(#{config.root}/extras) - # Activate observers that should always be running. - # config.active_record.observers = :cacher, :garbage_collector, :forum_observer - # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' @@ -44,6 +41,11 @@ module <%= app_const_base %> # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += [:password] + config.action_dispatch.default_headers = { + 'X-Frame-Options' => 'SAMEORIGIN', + 'X-XSS-Protection' => '1; mode=block' + } + # Use SQL instead of Active Record's schema dumper when creating the database. # This is necessary if your schema can't be completely dumped by the schema dumper, # like if you have constraints or database-specific column types. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 072aa8355d..2ffae66a57 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -20,9 +20,6 @@ # Generate digests for assets URLs. config.assets.digest = true - - # Defaults to nil and saved in location specified by config.assets.prefix - # config.assets.manifest = YOUR_PATH <%- end -%> # Specifies the header that your server uses for sending files. diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index dd3af86b99..c29fa4d95f 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -106,20 +106,28 @@ module ApplicationTests precompile! images_should_compile.each do |filename| - assert File.exists?("#{app_path}/public/assets/#{filename}") + assert_file_exists "#{app_path}/public/assets/#{filename}" end - assert File.exists?("#{app_path}/public/assets/application.js") - assert File.exists?("#{app_path}/public/assets/application.css") + assert_file_exists("#{app_path}/public/assets/application.js") + assert_file_exists("#{app_path}/public/assets/application.css") + + assert_no_file_exists("#{app_path}/public/assets/someapplication.js") + assert_no_file_exists("#{app_path}/public/assets/someapplication.css") - assert !File.exists?("#{app_path}/public/assets/someapplication.js") - assert !File.exists?("#{app_path}/public/assets/someapplication.css") + assert_no_file_exists("#{app_path}/public/assets/something.min.js") + assert_no_file_exists("#{app_path}/public/assets/something.min.css") - assert !File.exists?("#{app_path}/public/assets/something.min.js") - assert !File.exists?("#{app_path}/public/assets/something.min.css") + assert_no_file_exists("#{app_path}/public/assets/something.else.js") + assert_no_file_exists("#{app_path}/public/assets/something.else.css") + end + + def assert_file_exists(filename) + assert File.exists?(filename), "missing #{filename}" + end - assert !File.exists?("#{app_path}/public/assets/something.else.js") - assert !File.exists?("#{app_path}/public/assets/something.else.css") + def assert_no_file_exists(filename) + assert !File.exists?(filename), "#{filename} does exist" end test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do |