CNK's Blog

Rails Ajax options

Rails and JavaScript: JQuery, JSON, Oh My!

With Rails ‘respond_to’ method, it is really easy to return differnt kinds of data from the same controller method based on the url extension. I was really happy with this when I was building a a site to provide a read-only version of an event calendar I had built years earlier on the ArsDigita Community System. To get data out of that Oracle database, I built a Rails web site that let users ask for events by category, sponsor, or lecture series with a variety of date restrictions. The data was consumed by several different clients - two who wanted XML and several others who wanted JSON. I could use exactly the same URLs by just adding .xml or .json to the url. Even better, I could have controller render an html version of the same data which developers could use to preview the result sets. Once they had all the parameters the way they wanted, they just copied the url and added ‘.json’ ahd used it in their web pages.

But now I actually want to move into the 21st century and build some web pages with AJAX interactions. Rails has supported a variety of methods of working with Javascript starting with the RJS methods in the version 1.x days. Currently Rails (4.2.0) uses JQuery by default - including a jquery-ujs package which, when combined with the ‘remote: true’ argument to ‘form_for’, makes it simple to have your forms submit via AJAX instead of a normal server request.

First, the regular form_for helper:

    # This rails helper
    <%= form_for(@course) do |f| %>

    # Produces this html
    <form class="new_course" id="new_course" action="/courses" accept-charset="UTF-8" method="post">
      <input name="utf8" type="hidden" value="✓">
      <input type="hidden" name="authenticity_token" value="YE006c08mOnIYENufqJJZVrV3yAo1sHOgk9EJ9ZqigLIUktT6Vg0Px8FMx5GTl54ewIKYOaWqJYpWgiPEpQDZQ==">

But with ‘remote: true’ (note no authenticity_token):

    # This rails helper
    <%= form_for(@course, remote: true) do |f| %>

    # Produces this html
    <form class="new_course" id="new_course" action="/courses" accept-charset="UTF-8" data-remote="true" method="post">
      <input name="utf8" type="hidden" value="✓">

This comes into the server and is interpretted as a JS request:

    Started POST "/courses" for <ip> at 2015-03-12 23:07:10 -0700
    Processing by Empcms4Tutors::CoursesController#create as JS

which will fall into the format.js case in the normal respond_to stanza, which by default, will try to render the create.js.erb template in your courses views. This view should jquery directives that will be executed in the browser.

    $('#all_courses').append("<%= escape_javascript(render partial: 'table_row', locals: { course: @course }) %>");
    $('#course_name').val(''); // clear form input field
    $('#error_explanation').html(''); // clear error message

This flow has some pros and cons. On the bright side, it is relatively easy (as long as you remember to use ‘escape_javascript’ on any html you produce. But it means you have a fair amount of javascript scattered small files in your views directory. It is JS that manipulates your html so in that sense it is a view. But even if I am less than enthusiastic about that aspect, the big selling point is that I can reuse the code in my view partials. The exact same embedded ruby (erb) that created the table rows that loaded when the page first loaded is the erb that creates the new row JQuery inserts when I finish adding a new course.

Speaking JSON

If you want to keep all of your JavaScript together, for example if you are using a more extensive front end framework, then you may want to have your AJAX interactions communicate as JSON. The adjustments you need to make for that start with adding a data-type argument to your form tag:

    # This rails helper
    <%= form_for(@course, remote: true, format: json) do |f| %>

    # Produces this html
    <form class="new_course" id="new_course" data-type="json" action="/courses" accept-charset="UTF-8" data-remote="true" method="post">
      <input name="utf8" type="hidden" value="✓">

    # Which shows up as this on the server
    Processing by Empcms4Tutors::CoursesController#create as JSON

If you don’t add a different format section, this will also end up in the format.js section and will send back the jquery selectors and manipulations as before - with a Content-Type header of ‘text/javascript’. However, since the browser wasn’t expecting to get JavaScript, it doesn’t evaluate it (at least Chrome doesn’t). So I can see the server responding without error, and the data coming into the browser with a status code of 200. No JavaScript errors register in the console - but nothing happens on the page. All in all my least favorite type of error - no error, just nothing happens.

So to correct that, we add a ‘format.json’ section to our ‘respond_to’ block:

    format.json { render json: @course }

So now the browser gets back a JSON representation of the new item with a Content-Type header of ‘application/json’. Then it is up to us to write the client-side JavaScript - for example to add the table row with the new element. That might look something like this (untested code):

    $('#all_courses).on('ajax:success', function(event, data, status, xhr) {
                                        // add the table row with the course name, links, etc.
                    }.on('ajax:error', function(event, xhr, status,error) {
                                       // insert the failure message near the form
                                       $('form').append(xhr.responseText)
                    });

This keeps all your JavaScript on the client side - but at the expense of mixing some html into it as you build the new table row.

Upgrading to Yosemite (OsX 10.10)

Upgrading my laptop to Yosemite

We just got new Macs at work - which came with Yosemite preinstalled. There are a few odds and ends that I am not thrilled with, but overall I have found the transition from Lion to be quite smooth. So guess it is time to upgrade my personal laptop - before any more security issues are announced.

I went to the App Store and downloaded the installer and ran it. All seemed to be going well until the point where the installer thought it had “2 minutes left”. Two hours later, I gave up and went to bed, leaving it running. I have no idea when it actually finished but in the morning, my machine was asleep. When I woke it up, I got the odd gray screen overlay, with the blinking gray startup bars near the bottom - like you get when you have run the battery completely out. I was wondering if either the install had failed (probably not, since the screen behind the gray overlay was the new, flat, Yosemite look) or if the machine had overheated and crashed. It got very hot during the install but I had not heard the fan running. I did what I could to leave the bottom of the machine with good airflow so it could cool off but that was all I could do for that issue.

Anyway, despite the odd gray screen, the machine started up OK, let me log in, and then ran some “setting up your new mac” things. Looks OK. I launched the app store and installed the two security updates that have come out since Yosemite launched. I also downloaded the Mac office apps, pages, keynote, numbers….

XCode

I downloaded the newest XCode (6.1) and installed it. The first time it launched, I was asked to allow it to upgrade the device support - which it did and then promptly crashed. When I tried to launch it again, it crashed again. But third time is the charm. It again asked if it could run some (different) updates, but this time, it then gave me the normal XCcode interface. Until I get around to learning iOS programming, I don’t really have much use for the XCode GUI - except for using the iOS simulator as one of the targets for Karma to run JavaScript tests in a browser. The reason I need XCode is for the compiler. To get the command line tools installed, I ran xcode-select --install and agreed to install the command line tools. Then I ran xcodebuild --license. This was supposedly to accept the Xcode license - but just like when I did it for setting up my work machine, I got xcodebuild: error: invalid option '--license'. That Chris said that indicated that I had already accepted the license; not sure about any of that, but it doesn’t seem to have interfered with compiling stuff at work, so I am not going to worry about it.

X11

X11 no longer comes with OsX, but when I tried to use the X11 icon in my dock, it helpfully redirected me to this page which allowed my to download XQuartz 2.7.7 The installer ran just fine and at the end told me I had to log out and back in again to make this the default X11 for my machine.

Homebrew

I should have done a little checking and preparation before I did my upgrade. At least with Mac Ports, you generally can’t run any of the port commands after you have upgraded the OS. This means that it is really hard to figure out which packages you had installed (in particular, which were installed explicitly and which were installed because they were dependencies for something else you asked for) unless you ask for a list before doing the upgrade. While I was looking for blog posts about updating my homebrew-installed software, I found this blog post which explains why my Yosemite upgrade took FOREVER once it got to the 2 minutes left stages. Something about moving /usr/local and then moving all the files back… one by one. Oh well, at least now I know what that was about.

Fortunately, it looks like Homebrew will still basically work after the upgrade. I can run brew list and get a reasonable list of installed packages: emacs, docbook, graphviz, imagemagick, mongodb, mysql, node, postgresql, python, wget, etc. The blog post above recommended running brew doctor which gave me several sets of advice:

  • some broken symlinks to remove with brew prune
  • warnings about unlinked kegs
  • a keg-only formula for libxml2 that is linked into /usr/local
  • a warning that I should run brew update to get the latest formula info

The last one seemed reasonable, so I started there. The output of that command (and some reading of docs and the install script) explained why brew survived my upgrade. Homebrew just uses the system ruby - which is always there and should always run on the installed OS. And it explains why I don’t see separate ‘upgrade homebrew itself’ and ‘update the package list’ commands. Both are taken care of by running brew update which checks out the latest software and package information from Github:

    $ brew update
    Checking out files: 100% (1668/1668), done.
    Updated Homebrew from ef6d160e to d9a69a40.
    ==> New Formulae
    ==> Updated Formulae
    ==> Deleted Formulae

The first piece of advice also seemed reasonable - clean up dangling symlinks:

    $ brew prune
    Pruned 0 dead formulae
    Tapped 36 formulae
    Pruned 30 symbolic links from /usr/local

Then, I ran brew outdated to see what needs updating:

    $ brew outdated
    cairo (1.12.16_1 < 1.14.0)
    cmake (2.8.8, 2.8.12.2, 3.0.2 < 3.1.0)
    emacs (24.1, 24.3 < 24.4)
    ffmpeg (1.2.1, 2.2.3, 2.3.3 < 2.5.2)
    freetype (2.5.3_1 < 2.5.5)
    gettext (0.18.3.2, 0.19.2 < 0.19.4)
    git (1.8.0.2, 1.8.1.4, 2.0.0, 2.1.1 < 2.2.1)
    glib (2.42.0 < 2.42.1)
    gtk+ (2.24.24 < 2.24.25)
    harfbuzz (0.9.35 < 0.9.37)
    icu4c (53.1 < 54.1)
    imagemagick (6.8.0-10, 6.8.9-1, 6.8.9-7 < 6.8.9-8)
    libevent (2.0.21 < 2.0.21_1)
    libgpg-error (1.10, 1.13, 1.16 < 1.17)
    libksba (1.3.0, 1.3.1 < 1.3.2)
    libpng (1.6.10, 1.6.13 < 1.6.16)
    librsvg (2.36.3 < 2.36.3_1)
    libtool (2.4.2 < 2.4.4)
    libxml2 (2.9.1 < 2.9.2)
    mongodb (2.6.1, 2.6.4_1 < 2.6.6)
    mpfr (3.1.2, 3.1.2-p8 < 3.1.2-p10)
    mysql (5.5.25, 5.6.19, 5.6.21 < 5.6.22)
    node (0.10.9, 0.10.28 < 0.10.35)
    openssl (1.0.1e, 1.0.1h, 1.0.1i < 1.0.1j_1)
    postgresql (9.1.4, 9.2.4, 9.3.4, 9.3.5_1 < 9.4.0)
    python (2.7.5, 2.7.7_1, 2.7.8_1 < 2.7.9)
    redis (2.8.9, 2.8.10, 2.8.17 < 2.8.19)
    sqlite (3.7.15.2, 3.7.17, 3.8.4.3, 3.8.6 < 3.8.7.4)
    webp (0.4.0_1 < 0.4.2_1)
    wget (1.13.4, 1.15_1, 1.15_2 < 1.16.1)
    xvid (1.3.2 < 1.3.3)

I think I want to update just about everything. But I am a little concerned about upgrading the databases. Let’s start by pinning them, then upgrading everything else, and then upgrading them one at a time. But first, do they run now?

Looks like mysql is running - I see processes for mysqld_safe and mysqld in ps and I can log in as root and poke around. The postgresql server is not running at the moment and I mostly have not been using it. So I am just going to pin those two packages and upgrade everything else:

    $ brew pin mysql
    $ brew pin posgresql
    $ brew upgrade

    ==> Upgrading 29 outdated packages, with result:
    cairo 1.14.0, cmake 3.1.0, emacs 24.4, ffmpeg 2.5.2, freetype 2.5.5,
    gettext 0.19.4, git 2.2.1, glib 2.42.1, gtk+ 2.24.25, harfbuzz 0.9.37,
    icu4c 54.1, imagemagick 6.8.9-8, libevent 2.0.21_1, libgpg-error 1.17,
    libksba 1.3.2, libpng 1.6.16, librsvg 2.36.3_1, libtool 2.4.4, libxml2
    2.9.2, mongodb 2.6.6, mpfr 3.1.2-p10, node 0.10.35, openssl 1.0.1j_1,
    python 2.7.9, redis 2.8.19, sqlite 3.8.7.4, webp 0.4.2_1, wget 1.16.1,
    xvid 1.3.3

    ==> Not upgrading 2 pinned packages:
    mysql 5.6.22, postgresql 9.4.0

    Error: You must `brew link fontconfig pixman' before cairo can be installed

Then the updates all ran - some giving messages, mostly about how to launch daemons or warning you not to link certain libraries because they would conflict with the OsX versions. To pick up the last few upgrades, I did as suggested and ran brew link fontconfig pixman and then reran brew upgrade. I will save the output of all of these upgrades in case I need to go back to it.

MySQL

The MySQL upgrade is only one minor version different, so I think I’ll try to do that upgrade. About the only database I care about in the current one might be the cardsharp_dev database, so I am not too worried about the data even if there is some strange incompatibility.

    $ launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
    $ brew unpin mysql
    $ brew update mysql
    $ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

Logged in and looked around. Seems fine at least at a glance.

Starting and stoping services

While I was looking around for how to stop MySQL before the upgrade, I found two useful resources, this launchctl tutorial and this blog post on a brew command to replace all this: brew services

    $ brew tap homebrew/boneyard
    brew tap homebrew/boneyard
    Cloning into '/usr/local/Library/Taps/homebrew/homebrew-boneyard'...
    remote: Counting objects: 714, done.
    remote: Compressing objects: 100% (4/4), done.
    remote: Total 714 (delta 1), reused 0 (delta 0)
    Receiving objects: 100% (714/714), 171.26 KiB | 0 bytes/s, done.
    Resolving deltas: 100% (335/335), done.
    Checking connectivity... done.
    Tapped 65 formulae

    [08:51 PM] (brazen:~) $ brew services stop mysql
    brew services stop mysql
    Warning: brew services is unsupported and will be removed soon.
    You should use launchctl instead.
    Please feel free volunteer to support it in a tap.

    Stopping `mysql`... (might take a while)
    ==> Successfully stopped `mysql` (label: homebrew.mxcl.mysql)

    [08:52 PM] (brazen:~) $ brew services start mysql
    brew services start mysql
    Warning: brew services is unsupported and will be removed soon.
    You should use launchctl instead.
    Please feel free volunteer to support it in a tap.

    ==> Successfully started `mysql` (label: homebrew.mxcl.mysql)

Postgres

Perpahs I should leave it but I would really like to have everything upgraded for the start of the new year. I had trouble getting to old databases after a previous postgres upgrade. So first I want to take a backup. And to do that, I need to start the database server…..

    $ postgres -D /usr/local/var/postgres
    postgres -D /usr/local/var/postgres
    FATAL:  could not open directory "pg_tblspc": No such file or directory

It looks like the [Yosemite upgrade removed a few empty directories] (http://stackoverflow.com/questions/25970132/pg-tblspc-missing-after-installation-of-os-x-yosemite) that we need:

    $ mkdir /usr/local/var/postgres/pg_tblspc
    $ mkdir /usr/local/var/postgres/pg_twophase
    $ mkdir /usr/local/var/postgres/pg_stat_tmp
    $ postgres -D /usr/local/var/postgres
      LOG:  database system was shut down at 2014-10-02 22:09:43 PDT
      LOG:  creating missing WAL directory "pg_xlog/archive_status"
      LOG:  database system is ready to accept connections
      LOG:  autovacuum launcher started

I logged in and looked around. Looks like there are 2 tablespaces (\db) both owned by me - and a single schema (\dn) - also owned by me. What I missed, was how to list the databases: \l. So I missed doing the pg_dump before I upgraded the database. Fortunately there is an upgrade command which I found detailed at https://kkob.us/2014/12/20/homebrew-and-postgresql-9-4/.

    $ mv /usr/local/var/postgres /usr/local/var/postgres9.3
    $ brew unpin postgresql
    $ brew upgrade postgresql
    $ initdb /usr/local/var/postgres -E utf8
    $ pg_ctl -D /usr/local/var/postgres -l logfile start
    server starting
    $ psql
    psql: FATAL:  database "cnk" does not exist
    $ psql
    psql (9.4.0)
    cnk=#

    $ pg_ctl stop
    waiting for server to shut down.... done
    server stopped

    $ pg_upgrade -d /usr/local/var/postgres9.3 -D /usr/local/var/postgres \
     -b /usr/local/Cellar/postgresql/9.3.5_1/bin/ -B /usr/local/Cellar/postgresql/9.4.0/bin/ -v

    ... This did some checks then dumped the old databases:
        alensonline
        cms_dev
        cms_test
        cnk
        marysplace_dev
        marysplace_test
        postgres
        rails_rumble_13_development
        template1

There was a lot more output - and eventually the upgrade failed because I had already created the database ‘cnk’. So let’s remove the new stuff and try again:

    $ rm -rf /usr/local/var/postgres
    $ rm /usr/local/var/pg_upgrade_*
    $ initdb /usr/local/var/postgres -E utf8
    $ pg_upgrade -d /usr/local/var/postgres9.3 -D /usr/local/var/postgres \
     -b /usr/local/Cellar/postgresql/9.3.5_1/bin/ -B /usr/local/Cellar/postgresql/9.4.0/bin/ -v
    ......
    Upgrade Complete
    ----------------
    Optimizer statistics are not transferred by pg_upgrade so,
    once you start the new server, consider running:
        analyze_new_cluster.sh

    Running this script will delete the old cluster's data files:
        delete_old_cluster.sh

The end of the initdb output mentioned it was enabling ‘trust authentication’ for all local connections. I only want my user to be able to log into postgres. So before starting up the database, I edited /usr/local/var/postgres/pg_hba.conf to change the user from ‘all’ to ‘cnk’.

    $ pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/logfile start
    $ psql
    psql (9.4.0)
    Type "help" for help.

    cnk=# \l
                                           List of databases
                Name             | Owner | Encoding |   Collate   |    Ctype    | Access privileges
    -----------------------------+-------+----------+-------------+-------------+-------------------
     alensonline                 | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     cms_dev                     | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     cms_test                    | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     cnk                         | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     marysplace_dev              | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     marysplace_test             | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     postgres                    | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     rails_rumble_13_development | Mac   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
     template0                   | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/cnk           +
                                 |       |          |             |             | cnk=CTc/cnk
     template1                   | cnk   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | cnk=CTc/cnk      +
                                 |       |          |             |             | =c/cnk
    (10 rows)

    cnk=# \c alensonline
    You are now connected to database "alensonline" as user "cnk".
    alensonline=# \d
                      List of relations
     Schema |          Name           |   Type   | Owner
    --------+-------------------------+----------+-------
     public | assets                  | table    | cnk
     public | assets_id_seq           | sequence | cnk

Looks like everything made it.

Upgrading to Rails 4.2.0

Upgrading Rails Versions: 4.1.8 to 4.2.0

After a lot of interruptions for other work, last week I finally finished recreating my CMS app in Rails 4 - just in time for the release of Rails 4.2! Most of the information I have been reading about 4.2 has focused on new features, adequate record, foreign keys, active job, etc. So I was not really prepared for a number of issues that came up as a result of changes and depreciations. On my first pass, 871 of my 1419 tests failed! So guess I had better take some notes as I go along.

To get started, I changed the Rails version in my Gemfile from 4.1.8 to 4.2.0 and ran bundle upgrade rails and then ran my rspec test suite (rspec-rails 3.1.0 with rspec-core 3.1.2). As I said, a TON of tests failed, including the vast majority of my controller tests.

SSL Requirement

All (or nearly all) of the controller test failures pointed to my SSL-requirement gem. I need to go back and diagnose that - but since I can’t easily actually run SSL on my dev server right now, I commented out the gem in my Gemfile and include lines in my application controller:

    diff --git a/Gemfile b/Gemfile
    -gem 'bartt-ssl_requirement', :require => 'ssl_requirement'
    +# gem 'bartt-ssl_requirement', :require => 'ssl_requirement'

    diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb

    -  include ::SslRequirement
    -  ssl_allowed :all
    +  # include ::SslRequirement
    +  # ssl_allowed :all

Deprecation Warnings

New Configuration Options

Each time I run my tests, I was getting a couple of deprecation warnings, one about renaming a configuration option serve_static_assets to serve_static_files and one about suppressing errors raised within after_rollback and after_commit callbacks. The first one was fairly easy to deal with. I just searched my config directory and changed the option name in my test.rb and production.rb files. It isn’t set in development.rb so I continued to leave it out.

For the second warning, I tried adding an option in each of my 3 environments files - setting the option to true for dev and test but keeping the old exception suppression by setting this to false in my production.rb file.

    # Don't suppress errors in after_rollback and after_commit callbacks
    config.active_record.raise_in_transactional_callbacks = true

Unfortunately, setting those options did not fully suppress the deprecation warnings. I am still getting them for all 4 of the models I have that use paperclip (my documents model and all 3 of my image type models). Before reporting an issue, I thought I would try upgrading to the latest version of paperclip. Running bundle update paperclip upgraded me from paperclip 4.2.0 to 4.2.1 and cocaine 0.5.4 to 0.5.5. And that did remove the remaining ‘raise_in_transactional_callbacks’ warnings when I run my tests!!!

Truthiness

One of my tests had several instances of the following warning:

    DEPRECATION WARNING: You attempted to assign a value which is not
    explicitly `true` or `false` to a boolean column. Currently this value
    casts to `false`. This will change to match Ruby's semantics, and will
    cast to `true` in Rails 5. If you would like to maintain the current
    behavior, you should explicitly handle the values you would like cast
    to `false`.

All of these were coming from sections of the test where I was doing a create or update with invalid parameters. I had not given much thought to those params but assigned a nonsense value to one of my boolean columns:

    let(:invalid_attributes) { {:campus_only => 'banana'} }

The model in question is not super picky, so it is a little difficult to find parameters that would invalidate the model. For my create actions, sending in an empty file attribute would invalidate the model object. But for an update, an empty file would be perfectly legitimate since one can update other attributes without uploading a new file. But, since I am mocking out the save action to return false in my ‘update with invalid params’ tests, it is good enough.

Page Versions

When running the pages controller spec, I got a bunch of warnings about serialized_attributes. I suspect those are coming from my versioning system, paper_trail. But I noticed that there is also a new version of the tree plugin I use, closure tree. Looking at the changelog, I didn’t see anything that looked like it would make any difference but decided to upgrade anyway so as to keep up; this took me from closure_tree 5.0.0 to 5.2.0.

Upgrading paper_trail from 3.0.5 to 3.0.6 did not make any difference in the serialized_attributes warnings:

    DEPRECATION WARNING: `serialized_attributes` is deprecated without
    replacement, and will be removed in Rails 5.0. (called from block in
    update at /data1/srv/rails/empcms4/app/controllers/pages_controller.rb:62)

Looking at the issue queue, I guess I should have known about this for quite a while: https://github.com/airblade/paper_trail/issues/416 There doesn’t seem to be any consensus about a solution. For now I think I’ll ignore it - even though it means my page controller and versions controller specs are kind of noisy.

Errors

Changes to assert_select

Almost all of my request specs and a bunch of my view specs are failing with messages like:

    NotImplementedError:
       Implementing document_root_element makes assert_select work
       without needing to specify an element to select from.

Mass Upgrade of Gems

Fiddling with a few gem updates didn’t seem to be making any difference to this, so I decided to move my Gemfile.lock to Gemfile.lock.bak (for reference) and let bundler install the latest compatible versions of things. This made the following updates:

    acts_as_list           0.4.0        0.6.0
    aws-sdk                1.55.0       1.60.2
    columnize              0.8.9        0.9.0
    execjs                 2.2.1        2.2.2
    factory_girl_rails     4.4.0        4.5.0
    factory_girl           4.4.0        4.5.0
    jquery-rails           3.1.2        4.0.2
    jquery-ui-rails        5.0.0        5.0.3
    libv8                  3.16.14.5    3.16.14.7
    mini_magick            3.8.1        4.0.1
    mini_portile           0.6.0        0.6.1
    mysql2                 0.3.16       0.3.17
    nokogiri               1.6.3.1      1.6.5
    quiet_assets           1.0.3        1.1.0       (dev and test only)
    rspec-core             3.1.2        3.1.7
    rspec-expectations     3.1.0        3.1.2
    rspec-mocks            3.1.0        3.1.3
    rspec-support          3.1.0        3.1.2
    safe_yaml              1.0.3        1.0.4
    sass-rails             4.0.3        4.0.5
    sass                   3.2.0        3.2.2
    spring                 1.1.3        1.2.0
    sprockets              2.11.0       2.12.3
    uglifier               2.5.3        2.6.0
    webmock                1.18.0       1.20.4

I also commented out coffee-rails because I am not using coffeescript; this removed the coffee-rails, coffee-script-source, and subexec gems.

This upgrade took care of most of the errors I was seeing in my view specs. The few remaining problems pointed out actual errors - mostly some form tags that had been copied over from the rails 2.3 version of this project. I also had an error in the user mailer - again due to misuse of url_for to get an https url. I fixed those and now all of my model, mailer, routing, controller, helper, and view tests pass. So now it is just my request specs that are dorked:

    $ rspec spec/requests
    Finished in 11.54 seconds (files took 4.56 seconds to load)
    146 examples, 123 failures, 3 pending

Defining document_root_element

All the remaining errors still refer to document_root_element not being implemented. Grepping through the gems in my Rails 4.2 gemset, most references come from a new gem, rails-dom-testing. The README for the gem shows an example of using document_root_element - and refers us to the API docs in selector_assertions.rb. But even after reading that, I am somewhat unclear about what to do.

More poking around - by searching on github - turned up 2 pull requests: https://github.com/rails/rails/pull/17107 and https://github.com/rails/rails-dom-testing/pull/20 Again, very confusing but suggesting the need to add an alias to html_document - temporarily?

I looked at the other places where I found document_root_element in my rails 4.2 gemset. All 3 were places where a method by that name was defined, one each in actionpack-4.2.0/lib/action_controller/test_case.rb, actionpack-4.2.0/lib/action_dispatch/testing/integration.rb, and actionview-4.2.0/lib/action_view/test_case.rb. Two out of 3 of those defined it as:

    def document_root_element
      html_document.root
    end

So I tried dropping that into one of my test files and that caused the tests in that file to pass.

To include this in all my request specs, I created a module and included it only in my request specs:

    # spec/support/assert_select_root.rb

    module AssertSelectRoot
      def document_root_element
        html_document.root
      end
    end

    RSpec.configure do |config|
        config.include AssertSelectRoot, :type => :request
    end

With that addition, almost all of my request specs passed. I had a couple of places (in request specs and in my view specs) where I needed to change my html entities to the characters they evaluate to - as warned about in the release notes: http://edgeguides.rubyonrails.org/4_2_release_notes.html#assert-select After taking care of those issues, all my tests pass again!

Other

I still haven’t figured out the SSL requirement errors. But that is for another post.

After that, I may still want to look into rest of the release notes to see if there is anything else I should examine: And I should read through the upgrade guide to see what else I might want to deal with. There is also an intro to Rails 4.2 video

  • though I suspect that will be about the new features and not about some of the refactoring or internal changes.

Migrating to Rails 4.1 - Part-2

Asset Pipeline

OK after a Pythonic interuption and a great vacation, back to my Rails upgrade. The original project was built way before the introduction of the asset pipeline. We we did the last rebranding, I was going cross-eyed trying to find all the references to colors - esp. as there were a couple of itterations from the design firm about exactly which gray was what. So I made myself a global color and font variable list and started to use less in development - with Less.app to recompile the stylesheets on the fly. I could just continue doing that - but it seems a shame not to use one of the big improvements in the Rails ecosystem. And we could use some help to get better browser/proxy caching and reliable cache expiration upon update.

So, I copied my *.less files from ‘public/stylesheets/’ into ‘app/assets/stylesheets’, renamed them from ‘.less’ to ‘.css.scss’, and updated all the variable references from @thing to $thing. Since I want my global variables used everywhere, I want a global compilation scope. According to the ROR Guide to the asset pipeline, that means I should use SASS’s @import directives rather than the rails asset pipeline require mechanism. I thought I understood - but then I was just getting an application.css file with the naked ‘@import “whatever”’ lines. The part I had overlooked was changing ‘application.css’ to ‘application.sass’. Once I did that, I got my content - with the variables correctly interpolated. I was still missing a couple of images referenced in the stylesheets. To fix that, all I needed to do was change from hand coding the url attribute background: url('/images/mte/search_button.png') no-repeat; to using the image-url helper background: image-url("mte/search_button.png") no-repeat;.

Getting my fonts to show up was a little more confusing. I started with a blog post from Atomic Object. It was for Rails 3.1 but I don’t think there have been major changes to the asset pipeline for Rails 4.1 - and the css files they generated from Font Squirrel look a lot like the files we got from the design firm. In the current site, I have all the font information in /public/fonts. I initially tried moving that directory into /app/assets/ but I had trouble finding an import statement that got the SASS compiler to include them in the compiled application.css file; using a relative url @import "../fonts/Caslon" got SASS to include them. But then I had trouble with the font-url helper - couldn’t figure out how to specify the location of the actual font files. The easiest working combination seems to be to move the fonts directory to app/assets/stylesheets/fonts, import Vaud.css.scss using @import "fonts/Vaud", and then use font-urls like src: font-url('fonts/VaudBook-webfont.eot'); /* IE9 Compat Modes */.

Migrating to Rails 4.1 - Part 1

For a variety of reasons we postponed upgrading our custom CMS from Rails 2 to 3 for …. quite a while. Migrating on the bleeding edge is hard - but so is being so far behind. In particular, the advice to upgrade step by step, using the rails rake tasks to automate some things, doesn’t really work because it is too difficult to figure out compatible sets of plugins. For one thing, my testing frameworks have undergone drastic changes. So even if I can automate upgrading rails, I can’t run the tests to verify the upgrades. So I am going to just start over with the current version of rails: 4.1.0.

Using RVM, ruby-1.9.3-p484, and a fresh gemset (empcms4), I generated a new project. I don’t think turbolinks is going to be especially useful, so I removed it. I really like shoulda, so that’s a must. I am less adamant about test framework. I generally prefer the minitest assertion syntax but I just spent a frustrating hour trying to use MiniTest::Mock to get a controller destroy action to claim it failed so I could test the responder from Chapter 6 of Crafting Rails 4 Applications. So when I read that Thoughtbot uses shoulda + rspec, I decided to use rspec.

This blog post, Setting up the BDD stack on a new Rails 4 application, has some pretty good advise on how to set up the things I want to use. Remember to include ‘rspec-rails’ in both the test and development groups so generators will create rspec tests for things.

I regenerated the current pages model and added some not null constraints and default values. I like using FactoryGirl and have a number of utility methods I would like to transfer from my original codebase. I installed factory_girl_rails and then configured it as described in the Getting Started docs.