Skip to content

Instantly share code, notes, and snippets.

@nukturnal
Created November 11, 2020 01:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nukturnal/e6ada9ca1da443da6b74d9ec68bbd29d to your computer and use it in GitHub Desktop.
Save nukturnal/e6ada9ca1da443da6b74d9ec68bbd29d to your computer and use it in GitHub Desktop.
Rails 4.0.0 Upgrade Notes
  • Requirements

    • Must be on Ruby 1.9.3 or higher.
  • Upgrade Tools

    • Run rake rails:update as quick way to upgrade in place. Check your Git diff afterwards.
    • Use RailsDiff to compare changes between your version of Rails and Rails 4.0.0.
  • Upgrade Tips

    • I found it easiest to create a new Rails skeleton app and use it as my index when toggling between the Rails 4.0.0 skeleton and the older Rails 3.x.x app. You can then use Git to compare and adjust differences for each file copied and pasted.

    • When using the above technique, the following files are worth simply copying and pasting and then using Git to compare differences for anything you might not want to loose:

        /config/environments/*.rb
        /config/initializers/*.rb
        /config/application.rb
        /config/boot.rb
        /config.ru
      
  • Capistrano

  • /Gemfile

    • Remove the assets group wrapper from your Gemfile.
    • If using the sextant gem, remove it. You can now hit "/rails/info/routes" in development mode to see all routes.
  • /config/application.rb

    • Remove the Bundler asset requirements from the application.rb. Use Bundler.require(:default, Rails.env) instead.

    • Remove the following lines:

        config.active_record.whitelist_attributes
        config.filter_parameters
        config.assets.enabled
        config.assets.version
      
    • Use a "console do...end" block for console related tooling such as Pry, Wirb, Hirb, etc.

  • /config/environments

    • Remove the following from all environment *.rb files (unless otherwise noted):

        config.whiny_nils
        config.active_record.auto_explain_threshold_in_seconds
        config.assets.compress
        config.active_record.mass_assignment_sanitizer
        config.action_dispatch.best_standards_support
        config.assets.compressor (production only)
        config.active_record.mass_assignment_sanitizer (test only)
      
    • Add the following lines to all environments *.rb files (unless otherwise noted):

        config.eager_load = false (makes app thread safe, set to "true" for production)
        config.assets.js_compressor = :uglifier (production only)
        config.active_record.migration_error = :page_load (development only)
      
  • /config/initializers/secret_token.rb

    • Add line "config.secret_key_base" below "config.secret_token". Make sure to generate a new token for secret_key_base. Once fully migrated, the "config.secret_token" line can be deleted.
    • To genereate a new secret, use: "rake secret".
  • /config/initializers/filter_parameters.rb

    • Manually create this as it used to be defined in application.rb but should be defined as an initializer now.
      • Example: Rails.application.config.filter_parameters += [:password]
  • /config/routes.rb

    • Convert/remove all "match" methods to "get", "post", etc. methods (or use the "via" key in conjunction with "match").
    • Rename uses of "put" to "patch".
    • Use concerns for duplicated nested routes. Example: concern :commentable { resources :comments } resources :posts, concerns: :commentable resources pages, concerns: :commentable
    • Use constraints to force them into URL helpers.
      • Example: get "test", to: "test#index", constraints: {protocal: "https", subdomain: "test"}
      • Yields: app.test_url = "https://test.example.com/test"
  • /app/controllers/application_controller.rb

    • Add "protect_from_forgery with: :exception". This is the default for new rails apps. Throws an exeption when CSRF attacks are encountered.
  • ActiveRecord

    • Remove all .find_all* dynamic methods, use .where methods instead.

    • Use .find_by, .find_or_initialize_by, or .find_or_create_by instead of the dynamic method missing methods used in the past. The implementations are much cleaner and efficient.

      • Example: Post.find_by(name: "test")
    • The .find_by method is a wrapper to the .where method and takes the same arguments. Think about using .where instead of the .find_by methods instead.

    • The .first and .last methods still exist but consider using them in conjunction with where clauses. Example: Post.where(title: "Example").first.

    • The .first_or_create and .first_or_create! methods have been added so that if the first occurance is found, grab it. Otherwise, create and return it. Example: Post.where(title: "Example").first_or_create.

    • Remove all attr_accessbile whitelists. Use strong parameters via your controller instead.

    • Use lambdas (->) for all scopes.

    • Use .all instead of .scoped as the .scoped method is deprecated. It will return an ActiveRecord::Relation object.

      • Use .all to assign to an instance variable but maintain a lazy loaded ActiveRelation array.
      • Use .all.to_a to force load an array of ActiveRecord objects.
    • Use .load to force load (instead of lazy load) an array of ActiveRelation objects.

    • Use .none to return an empty ActiveRelation array.

    • Use .not to build negative queries.

      • Example: Post.where.not(name: "test") = "SELECT * FROM posts WHERE posts.name != 'test'"
    • The .order method can take a hash now.

      • Example: .order(name: :desc, label: :desc)
    • Use the bang methods to build ActiveRelation queries without having to re-assign to a variable.

      • Example: posts = Post.all; posts.where!(label: "Test") -- This will modify, in place, the "posts" variable with conditions from the "where" statement applied.
    • Use .references when building eager loaded queries. Example: Post.includes(:comments).where("comments.label = 'Example'").references(:comments). This explicitly informs the relation what is being referenced to build a proper SQL query.

      • .references is not necessary when using hash-based conditions. Example: Post.includes(:comments).where comments: {label: "Example"}
      • .references is not necessary when no conditions are provided. Example: Post.includes(:comments).order "comments.label".
    • Use .update instead of update_attributes. This will apply validations and callbacks.

    • Use .update_columns instead of .update_attribute or .update_column. This will not apply validations or callbacks.

    • Use "\A" and "\z" instead of '^' and '$' for single line string regular expresion evaluations in validations to prevent this error message: "ArgumentError: The provided regular expression is using multiline anchors (^ or $), which may present a security risk. Did you mean to use \A and \z, or forgot to add the :multiline => true option?"

    • When enhancing Ruby objects to behave like ActiveModel objects, use "include ActiveModel::Model" instead. This one-liner is equivalent to writing the following:

        extend ActiveModel::Naming
        extend ActiveModel::Translation
        include ActiveModel::Validations
        include ActiveModel::Conversion
      
    • Use ActiveRecord::Store for easily creating accessors to a Hash for an ActiveRecord object. For example, add "store :settings, accessors: [:language, :timezone]" to a Example class. Then, the "example" instance can use example.language or example.timezone. This is also handy when using PostgreSQL hstore columns.

  • ActionController

    • Use strong parameters. Example: params.require(:my_params).permit :label, :description
      • .require enforces a parameter to be present.
      • .permit enforces only certain parameters to be passed through. Handy for limiting which attributes are allowed for mass assignment.
      • By default the following types are permitted:
        • String
        • Symbol
        • NilClass
        • Numeric
        • TrueClass
        • FalseClass
        • Date
        • Time
        • DateTime
        • StringIO
        • IO
        • ActionDispatch::Http::UploadFile
        • Rack::Test::UploadedFile
      • By default, all unpermitted parameters will be logged. Use "config.action_controller.action_on_unpermitted_parameters = :raise" in application.rb to raise exceptions instead.
    • Rename "before_filter" and related filters to "before_action".
    • Use .add_flash_types to add custom flash notifications. Example: add_flash_types :example.
      • Custom flashes can be set for display, within a controller action, via the following ways:
        • flash[:example] = "Example!"
        • redirect_to @example, example: "Example!"
    • Use .fresh_when to set the "etag, last_modified, or both on the response and renders a 304 Not Modified response if the request is already fresh." In other words, if the ETag sent from the client matches what is generated by the controller via the fresh_when method then the client cache is used instead of the controller rendering the same view again. Example:
      • @example = Example.find params[:id]
      • fresh_when @example
    • Use .etag to avoid defining .fresh_when within every action of a controller for the same object. Example: etag {current_user.id}
    • Live Streams
      • Requires a web server like Puma.

      • Example Controller Code:

          class TasksController < ApplicationController
            include ActionController::Live
        
            def index
              response.headers["Content-Type"] = "text/task-stream"
              Task.all.each {|task| response.stream.write task.label}
              response.stream.close
            end
          end
        
      • Example JavaScript Code:

          $(function() {
            function initialize() {
              var source = new EventSource("/tasks");
              source.addEventListener("message", update);
            };
        
            function update(event) {
              var task = $("<li>").text(event.data);
              $("#tasks").append(task);
            }
          });
        
  • ActionView

    • Use .date_field to get an HTML5 date field which can yield a date selector by default in modern browsers.

    • Use Ruby in the view templates by naming templates as follows: example.html.ruby, example.json.ruby, etc.

    • Custom flashes can be called directly by name within a view. Example (based on the controller example provided earlier):

        <div><%= example %></div>
      
    • Use .collection_select to build an array of select options for a has_many assocation. Example:

        <%= collection_select :project, :task_id, Task.all, :id, :label %>
      
      • Use .collection_radio_buttons to build a collection of radio buttons using the same arguments shown above.
      • Use .collection_check_boxes to build a collection of check boxes using the same arguments shown above.
    • Use cache to create a cached view fragment.

      • Example Code:

          <% cache task do %>
            <li><%= task.description %> - <%= task.owner_full_name %></li>
          <% end %>
        
      • The generated cache key for the task will consiste of the following:

        • Example: task/2-2013101050000/d9fb66b120b61f46707c67ab41d93cb2.
          • Segment 1: The object name.
          • Segment 2: The object id and updated_at date/time stamp.
          • Segment 3: The MD5 hash of the template fragment.
      • Things to know:

        • The MD5 hash portion of the cache key for the parent fragment is the combination of the parent, itself, and all child fragments.
        • If a child fragment is changed, the parent cache key will change too.
        • Cache digests will not work unless the rendering of a partial is explicitly defined (either is a valid option):
          • Option 1:

              <%= render partial: "task", collection: @tasks %>
            
          • Option 2:

              <%# Template Dependency: task %>
              <%= render @tasks %>
            
    • Turbolinks

      • By default, Turbolinks will hook into all links.

      • Use data: {"no-turbolink": true} to disable turbolink functionality (i.e. force full page refresh) for a specific link or a container of links.

      • Configure the application.js as follows:

          //= require jquery
          //= require jquery.turbolinks
          //= require jquery_ujs
          //= require turbolinks
        

        NOTE: Requiring jquery.turbolinks allows jQuery's .ready() method to fire after "page:load".

      • Because Turbolinks make AJAX requests to the server, there might be a reponse delay. The following can let the user know what is happening:

          // Show a "Loading..." indicator for a new page being fetched.
          $(document).on("page:fetch", function() {
            $("#load").show();
          });
        
          // Disable the page "Loading..." indicator when the page has changed.
          $(document).on("page:change", function() {
            $("#load").hide();
          });
        
  • Test::Unit

    • Uses minitest under the covers.
  • RSpec

    • "mock" is deprecated, use "double" instead.
    • "any_number_of_times" is deprecated, use "stub" instead.
  • PostgreSQL

    • To create an hstore in a migration, do the following:
      • Add the "execute 'create extension hstore'" line first.
      • Then pass "hstore", the name of your variable. Example: t.hstore :properties
    • To create a native array in a migration, do the following as an example: "t.string :tags, array: true"
  • Gems

  • Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment