Migrating from Carrierwave to ActiveStorage in Rails

For the Video Upload Platform series that I have been working on (Sorry I haven’t updated in a bit), I had been using Carrierwave to manage the file uploads. It also has a bunch of plugins/gems available to help with some tasks, however I wanted to move the series more into as “What is actually happening” set of posts. For this reason, I have decided to migrate that project from using Carrierwave to using ActiveStorage, since that has been released somewhat recently and is build into Rails.

If you have been following along in that series, then it should be pretty straight forward using the details I’m about to go into. If not, thats ok too, it should still be pretty familiar to you. At any rate lets get started!

The first thing we want to make sure is that we are update to date with Rails. If you have a specific version set in your Gemfile, remove that, or update it to the latest:

# Gemfile

- gem "rails", "~> 5.1.6
+ gem "rails"

One we have that updated, we just need to bundle the project to pull all the new fun in

bundle

Ok great. Now we want to add the ActiveStorage migrations and configure the set up to use local storage for our example. Do this by running the following command

bundle exec rails active_storage:install
bundle exec rake db:migrate

Great, now we should have our database migrated to contain the ActiveStorage tables. Let create the proper config file that ActiveStorage uses now. Create the file config/storage.yml and paste in the following YAML:

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

This tells ActiveStorage to store things locally. If you are interested in using some type of other storage methods, check out the ActiveStorage Overview page. We only have one more configuration step to go, and we should be good. Add the following line to your config/environments/development.rb file (test.rb and production.rb) as well if you want:

config.active_storage.service = :local

Now that we have ActiveStorage all set up, we can go ahead and update our model to migrate from Carrierwave to ActiveStorage. I will show our model and the line we want to remove and add:

class Video < ApplicationRecord
-  mount_uploader :file, VideoUploader
+  has_one_attached :file
end

As you can see, we no longer are going to use the CarrierwaveUploader. We should now be able to upload files using the same controller and form uploads that we had already created. The last thing we need to change however, is the view. We will pass the url for the upload using some built-in Rails helpers. In the app/views/videos/show.html.erb file, we need to make this change:

<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @video.title %>
</p>

<p>
  <strong>File:</strong>
-  <video controls width="640" height="480" src="<%= @video.file.url %>"></video>
+  <video controls width="640" height="480" src="<%= url_for(@video.file) %>"></video>
</p>

<%= link_to 'Edit', edit_video_path(@video) %> |
<%= link_to 'Back', videos_path %>

The last piece that we need to add in, which I will do in another post, is the ability to do the background Transcoding, that we were getting out of the box with the carrierwave-video gem, but this should get us starting with using ActiveStorage instead of Carrierwave.

Let me know if you have any thoughts in the comments below, or run into any issues. Thanks!

How to organize a routes.rb file in Rails

If you have a small Ruby on Rails application, you probably don’t have much need to organize your routes.rb file. However if your project is large, you probably have a somewhat complicated, and quite frankly, messy routes file. Sometimes its hard to really understand where your routes are, especially when you are working with hundreds of lines of routes. I had such an issue in a recent project I was working on.

The project has multiple contexts within it, and each on of them has their own set of routes. These were all smushed into the main routes.rb file along with the normal base routes. What I wanted to do was create multiple routes files under a config/routes folder and split them up there to make them more manageable. Here is an example of how to split them up:

# config/routes.rb
Rails.application.routes.draw do
  # Some base routes here
  resources :pages


  extend AdminRoutes
  extend UserRoutes
  extend OrganizationRoutes
end

As you can see, we have a basic routes file, however you will notice that there are a few extend Class directives in there. These will load in the corresponding class file, that I will have stored in the config/routes folder. Here is an example of what one of those files looks like.

# config/routes/admin_routes.rb

module AdminRoutes
  def self.extended(router)
    router.instance_exec do
      authenticate :user, lambda { |u| u.admin? } do
        mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
        mount Sidekiq::Web => '/sidekiq'
      end
    end
  end
end

I will show you one more, since the AdminRoutes file is mostly just the entry point for some mounted Rails Engines, and not normal resources

# config/routes/user_routes.rb

module UserRoutes
  def self.extended(router)
    router.instance_exec do

      authenticated :user do
        root to: "dashboard#show", as: :authenticated_root
      end

      devise_for :users,
        :skip => [:registrations],
        :controllers => { :invitations => 'user/invitations' }
      as :user do
        get '/user/new' => 'user/registrations#new', as: 'new_user_registration'
        post '/user' => 'user/registrations#create', as: 'user_registration'

        get 'user/edit' => 'user/registrations#edit', :as => 'edit_user_registration'
        put ':id' => 'user/registrations#update', :as => 'registration'

        delete '' => 'user/registrations#destroy'
      end

    end
  end
end

The last thing we need to do, is make sure that the new config/routes folder gets picked up by rails. Add the following line to your config/application.rb file

config.autoload_paths += %W(#{config.root}/config/routes)

Hopefully these examples help you with splitting up a large routes.rb file into smaller, more manageable pieces. Let me know in the comments if this helps, or you have any issues getting this to work.

WordPress/Nginx response long pauses

I have been working more and more with WordPress lately, and since I am into technology, I would have to host it myself. Right? 🙂 Anyway, I recently ran into an issue that took me a while to figure out. Well, almost fully figured out. At least I have mitigated the main issue. Here is it…

I was noticing that my page would load at a decent speed, nothing to cause alarm. However, when I would access individual pages, they would sometime (usually) take 15 to 20, and sometimes longer, seconds to load. I would confirm the same by using curl to request the page. The odd thing I noticed doing this, was that only part of the document was returned, and then there was a long pause, and finally the rest of the document came.

When this first started happening, I had suspected maybe there was something wrong with my nginx proxy settings, since I was passing PHP requests off to PHP-FPM. I tweaked pretty much every setting I could find and nothing helped. Then I started looking at PHP settings, maybe there wasn’t enough memory and it was buffering to disk. Nothing helped there either. I had other WordPress sites as well. Some were exhibiting the issue and some were not. I figured the issue had to be somewhere in WordPress, but wasn’t sure where yet.

I started by deactivating a bunch of plugins on my site to see if there was an issue there. It didn’t seem to help, although I could have just been a fluke, since sometimes the pages would work fine. This led me to believe it might have been network related somehow, but since the pages would still hang for a bit during a curl request I had suspected that it might have been more of a backend thing. It took some digging, but I finally searched for the right terms in the big Google, and case across an article that helped out, https://deliciousbrains.com/wp-migrate-db-pro/doc/wp_http_block_external/.

After I implemented this change in my WordPress blog, pages were loading lightning fast again. The only word of caution I should use is that this blocks all external requests, including plugin calls and updates, so you will want to combine this change with the WP_ACCESSIBLE_HOSTS setting which is also mentioned in that page.