diff options
20 files changed, 155 insertions, 562 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 236768227c..7a9c2a3fa9 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,27 @@ ## Rails 4.0.0 (unreleased) ## +* 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 +168,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_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/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/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/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 70bd1bf4d3..3e5a53b0ff 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,8 @@ ## Rails 4.0.0 (unreleased) ## +* ActiveSupport::JSON::Variable is deprecated. Define your own #as_json and #encode_json methods + for custom JSON string literals. *Erich Menge* + * Add String#indent. *fxn & Ace Suares* * Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument. *David Celis* 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..1ee90e88f2 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)' |