Skip to main content

Benjamin Oakes

Photo of Ben Oakes

Hi, I'm Ben Oakes and this is my geek blog. Currently, I'm a Ruby/JavaScript Developer at Liaison. Previously, I was a Developer at Continuity and Hedgeye, a Research Assistant in the Early Social Cognition Lab at Yale University and a student at the University of Iowa. I also organize TechCorridor.io, ICRuby, OpenHack Iowa City, and previously organized NewHaven.rb. I have an amazing wife named Danielle Oakes.

Embracing Change: Rails 5.1 Adopts Yarn, Webpack, and the JS Ecosystem

by Ben

With the inclusion of webpack and Yarn, the Rails team is showing a great deal of maturity and humility, recognizing that the web is no longer rendered only from the server, but also dynamically from the client. Not only has the team embraced these changes, but they are incorporating them in such a way as to make it feel like there’s a “Rails Way” to do JavaScript.

Source: Embracing Change: Rails 5.1 Adopts Yarn, Webpack, and the JS Ecosystem – Pixelated

chrismccord/render_sync

by Ben

Real-time Rails Partials

Source: chrismccord/render_sync

Google Cloud Platform Blog: Ruby on Google App Engine goes beta

by Ben

We’re thrilled to welcome Ruby developers to the Google Cloud Platform, and we’re committed to making further investments to help make you as productive as possible. This is just the start — stay tuned to the blog and our GitHub repositories to catch the next wave of Ruby support on GCP.

Source: Google Cloud Platform Blog: Ruby on Google App Engine goes beta

Thursday, 11/6: ICRuby Meetup

by Ben

From my posts on the Meetup event:

Hello Rubyists,

It’s almost the first Thursday of the month again. We’re always interested in having members present Ruby-related topics to the group, so if you’ve been playing with anything cool, please consider presenting! We’re pretty informal, so anything you present doesn’t have to be a “polished” keynote-style presentation; it can be as simple as plugging in your laptop and showing some code you’ve been working on.

If anyone would like to present something, please leave a comment [on the Meetup event].

Best wishes,

Ben

And also, my customary monthly Ruby news roundup:

Some Ruby news from the last month:

  • Ruby 2.1.4 released, along with patchlevel-releases for 2.0.0 and 1.9.3 containing security fixes
  • OSX 10.10 has shipped, with Ruby 2.0.0 as the default (Ruby 1.8.7 was completely removed)
  • Rails 3.2.20, 4.0.11, 4.1.7 released (security fixes) on Oct 30th
  • Last Rails 4.2.0 beta released, release candidates coming soon.
  • Ruboto 1.2.0 released, with support for ART on Android Lollipop (5.0)
  • Raptor — new Ruby web server — coming out this month. Supposed to be high performance.

ActiveRecord partial_updates broken when duping with Single Table Inheritance

by Ben

We ran into a strange bug in ActiveRecord when upgrading our Rails 3.2.x app to Rails 4.0.x. The bug was that we would dup a record, change some values, save it, and only the values we had changed would be used in the INSERT statement. That is, none of the values that came from the duped object would be used. It turned out to be a bug in the implementation of partial_updates in ActiveRecord, but the cause wasn’t as obvious as we first thought.

Because we tracked it down to partial_updates, our first inclination was just to disable partial_updates like so:

ActiveRecord::Base.partial_updates = false 

That definitely worked, but seemed like disabling a default Rails feature shouldn’t have been necessary. We tracked the problem down so that we could keep using partial_updates.

Here’s some code that reproduces our issue:

# class CreateTestUsers < ActiveRecord::Migration
#   def change
#     create_table :test_users do |t| 
#       t.string :name, :null => false
#       t.string :email
#       t.string :type
#   
#       t.timestamps
#     end 
#   end 
# end 

class TestUser < ActiveRecord::Base
  validates :email, :presence => true
  validates :name, :presence => true
end 

class UserA < TestUser
end 

class UserB < TestUser
end

original_user = UserA.create(:email => 'johndoe@gmail.com',
                             :name => 'John Doe')

dup_user = original_user.dup.becomes(UserB)
dup_user[:type] = 'UserB'
dup_user.email = 'dup@gmail.com'
dup_user.save! # exception!
# ActiveRecord::StatementInvalid: Mysql2::Error: Field 'name' doesn't have
# a default value: INSERT INTO `test_users` (`created_at`, `email`, `type`,
# `updated_at`) VALUES ('2014-03-03 16:33:23', 'dup@gmail.com', 'UserB',
# '2014-03-03 16:33:23')

Certainly not the most common of use cases; it was clear that STI was causing the issue as we worked on creating our minimal test case.

After reviewing how becomes works, we came to the conclusion that it was mangling the state of the object in the way that caused the bug. As you might expect, changing the call order made the difference.

This alternative works with partial_updates enabled:

dup_user = original_user.becomes!(UserB).dup
dup_user.email = 'dup@gmail.com'
dup_user.save!

One could argue that there’s still a bug to fix in Rails, but this is such a corner case that we probably have one of very few codebases that had this issue. We might take the time to submit a pull request, but a blog post is a lot more likely to help someone else in the meantime.

Versions:

A Huge List of Free Programming Books

by Ben

A Huge List of Free Programming Books.

Lots of topics. Not all are suitable for offline- or tablet-reading (web-only). Many PDFs are marked with “(PDF)”.

Phone/Phoner gem: SyntaxError on Rails 3.2.16, 3.2.15, and possibly before

by Ben

From an issue I opened today. We ended up switching to phony instead. Just a reminder to everyone out there that eval is evil!

While upgrading a project to 3.2.16 we ran into the following error:

syntax error, unexpected end-of-input
          unless defined? @@{:instance_writer=>false}

We tracked it down to monkey patching of cattr_accessor. It looks like some code was copied out of ActiveSupport, but ActiveSupport changed to allow a second argument (e.g., {:instance_writer=>false}). The use that’s causing the error is in ActiveRecord::Base.

Backtrace:

[...]/gems/phone-1.2.3/lib/support.rb:47: syntax error, unexpected end-of-input
          unless defined? @@{:instance_writer=>false}
                            ^
        from [...]/gems/phone-1.2.3/lib/support.rb:47:in `block in cattr_accessor'
        from [...]/gems/phone-1.2.3/lib/support.rb:46:in `each'
        from [...]/gems/phone-1.2.3/lib/support.rb:46:in `cattr_accessor'
        from [...]/gems/activerecord-3.2.15/lib/active_record/base.rb:339:in `<class:Base>'
        from [...]/gems/activerecord-3.2.15/lib/active_record/base.rb:333:in `<module:ActiveRecord>'
        from [...]/gems/activerecord-3.2.15/lib/active_record/base.rb:33:in `<top (required)>'

I hope this helps in providing a fix (or at least for other people who run into the problem).

Responsibility-centric vs. data-centric design

by Ben

Issue 4.11: Responsibility-centric vs. data-centric design – Practicing Ruby.

A good read; got me thinking about how we should structure our coming projects.  However, my feeling is that in a Rails app, a hybrid approach seems to work well… leave persistence and querying to ActiveRecord objects, and use services in the way this article suggests.

Using Heroku with an external MySQL database

by Ben

We recently had a need to use Heroku with an external MySQL database. Thankfully, there was already a gem that solved the problem. However, it didn’t have that much by the way of documentation, so I wrote this up and contributed it to the author. Also, I added X.509 support, which we required.


NOTE: After my pull requests are merged, you should look to the main repository’s README which has a copy of the below.


Example: MySQL with a CA Certificate

In this example, we are setting up a connection to an external MySQL server using the default initializer.

What we have:

First, we configure Heroku with the appropriate environment variables:

# You may have to specify the app name or remote name here via --app or --remote, respectively
heroku config:add EXTERNAL_DATABASE_CA='ca-cert.pem'
heroku config:add EXTERNAL_DATABASE_URL='mysql://username:password@server/dbname'

By default, heroku_external_db looks for the CA cert in
config/ca, so we need to commit it:

mkdir -p config/ca
cp path/to/ca-cert.pem config/ca
git add config/ca
git commit -v # Using -v since we want to make sure the contents are what we expect (e.g. not a private key)

Additionally, we need the mysql gem in our Gemfile since we are setting up a MySQL server:

echo "gem 'mysql', '~> 2.8.1'" >> Gemfile
bundle install # Need Gemfile.lock too

Keep in mind that Heroku installs its own database.yml for Rails apps and we have to install pg as well. Unfortunately, shared databases are mandatory (but are free).

$ heroku addons:remove shared-database:5mb
-----> Removing shared-database:5mb from our-app... failed
 !     Shared databases cannot be removed

PostgreSQL may still be useful to you if, for example, you want to have feature toggles in a local database, but the main data kept externally. However in our case, it also means all developers will need MySQL and PostgreSQL running locally, which is unfortunate.

One workaround is only installing pg in production:

# File: Gemfile

# *Only* needed on production.
group :production do
  gem 'pg', '~> 0.11.0' # Regardless of whether you plan to use the database or not, Heroku requires you have 'pg' installed.
end

Then bundle like so:

bundle install --without production

With our gems updated, commit:

git commit -av # Commit Gemfile and Gemfile.lock

Another option may be overriding database.yml somehow, although it would add complexity. For more info, please see stackoverflow.com/questions/4204724/strategies-for-overriding-database-yml.

With our dependencies out of the way, we can move on to testing the connection.

If you are making a new application, you may wish to have a simple MVC for testing that the connection works. E.g., for a blog style application with posts do:

rails generate scaffold post
# NOTE you probably want to change the default "Post.all" to "Post.limit(5)" or something similar
git add .
git commit # ...
# Don't forget to set a default route, etc.

With all these changes committed, we can deploy to our Heroku app:

git push heroku master # Your remote may be different

Now, since we are connecting to an existing database, we don’t need to run any migrations. (Keep in mind that when sharing a database, it is best to have one authoritative source for migrations to live.) If in your situation you’re creating a new database, you may need to do that, run migrations, seed the database, etc at this point.

Open our-app.heroku.com and we should see our data. If you happen to run into a problem, please check the logs first:

heroku logs --tail # Again, you may need to specify an app

If you are having a problem, a good starting point is double checking your passwords, usernames, security settings, etc.

Example: MySQL with X.509

The process is very much the same as the above example, except two extra environment variables and files are required. Below are the extra steps.

What we have:

First, we configure Heroku with the appropriate environment variables:

heroku config:add EXTERNAL_DATABASE_CA='ca-cert.pem'
heroku config:add EXTERNAL_DATABASE_CERT='client-cert.pem'
heroku config:add EXTERNAL_DATABASE_KEY='client-key.pem'

By default, heroku_external_db looks for the files in config/ca, so we need to commit them:

mkdir -p config/ca
cp path/to/ca-cert.pem path/to/client-cert.pem path/to/client-key.pem config/ca
git add config/ca
git commit

The rest of the process is the same as in “MySQL with a CA Certificate”.

Reactions

ActiveRecord::ConnectionNotEstablished in Rails 3.1 on Heroku

by Ben

Other languages: 日本語

We have a simple application that doesn’t have an ActiveRecord dependency. It’s deployed to Heroku, and it’s been working fine on Rails 3.0.x since April 2011. We knew we weren’t using ActiveRecord for database connectivity, but we let it be, since it wasn’t causing any issues.

When upgrading to Rails 3.1, we found that every single page would give ActiveRecord::ConnectionNotEstablished on our staging environment on Heroku. The same error didn’t happen in development. Although we might have been able to get gem 'pg' set up and working, we really didn’t need an ActiveRecord dependency at all.

I found part of a solution on StackOverflow, but it needed a little tweaking for Rails 3.1.0.

# File: config/application.rb

# Pick the frameworks you want:
# require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "active_resource/railtie"
require "sprockets/railtie"
require "rails/test_unit/railtie"

That’s what Rails 3.1.0 generates when running rails new myproject --skip-active-record. (Note that require "active_record/railtie" is commented out.) This solved our ActiveRecord::ConnectionNotEstablished problem, but gave us a few others, namely:

Fixtures

There’s some normal stuff to get rid of in terms of spec_helper.rb and/or test_helper.rb. Here’s an example:

# File spec/spec_helper.rb

# # If you're not using ActiveRecord, or you'd prefer not to run each of your
# # examples within a transaction, remove the following line or assign false
# # instead of true.
# config.use_transactional_fixtures = true

You may have others. Tarantula had to be adjusted for us, for example.

ActiveRecord::RecordNotFound

ActiveRecord::RecordNotFound, however, was an interesting problem. Everything worked fine without ActiveRecord except the places where we were using ActiveRecord::RecordNotFound to give a HTTP 404 to the user agent. That seems strange in a lot of ways, because a 404 shouldn’t have anything to do with your chosen ORM. My first intuition was to do require 'active_record/errors' (see also the Rails docs), but that caused problems with assumptions in 'rspec/rails'.

Right now, the below is what we ended up with:

# File: config/application.rb

# Pick the frameworks you want:
# require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "active_resource/railtie"
require "sprockets/railtie"
require "rails/test_unit/railtie"

# For errors like ActiveRecord::RecordNotFound
require "active_record"

Our automated tests (Rspec, integration tests, Tarantula, Selenium, etc) all pass with it and we no longer get ActiveRecord::ConnectionNotEstablished, but we still have an ActiveRecord dependency I don’t like. (There must be another error we can raise — I don’t entirely like the render '/404.html', status: 404 solution for several reasons.)

Update:

This blog post has been translated/incorporated into a Japanese blog post.

Reactions