kyleke.es
I'm Kyle Keesling, an Indianapolis-based Rubyist & Full-stack Developer

An Easier Way to Accommodate Deletion of ActiveStorage Attachments

December, 3 2018 -

A Little Background

As I continue to phase out Paperclip in favor of ActiveStorage, I’ve wanted to keep the methods I used to manage these assets as succinct and reusable as possible.

ActiveStorage gives us a dead simple way to save and update assets, but the ability to delete assets independently of the parent record, particularly if you’re using has_many_attached, has been left to each individual app to figure out.

I have stumbled upon a couple of different takes on how to potentially do this, but I’ve not been satisfied with their approaches. Many do not take into account authorization and permissions, which if you’re not careful, allows any user to delete any attachment they choose just by randomly hitting URLs in your application. With that in mind I set out to try and roll my own.

The Approach

Since all attachments are saved as the same object/models types, Attachment and Blob, we can use a common controller to interact and modify with them, regardless of the parent record that it belongs to.

In my case I chose to stick closely to the naming conventions already given to us, so I created a new controller named ActiveStorage::AttachmentsController, and since for now I’m only worried about deleting attachments, we only need one method, delete.

I use Devise for authentication, so we need to make sure that the user is logged in before we let them do anything, which we do with :authenticate_user!.

I also want to make sure that the user has the permissions to modify these attachments, so I check their permissions to see if they can modify the parent record using the authorize_attachment_parent! method. I use CanCanCan in this case, but you can always swap out your auth call to fit whatever method you use. This piece is critical to ensure that your users aren’t deleting anything they shouldn’t.

The purge_later call will take care of the actual file deletion in your background queue, and will delete the corresponding Blob record.

# app/controllers/active_storage/attachments_controller.rb
class ActiveStorage::AttachmentsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_attachment, :authorize_attachment_parent!

  def destroy
    @attachment.purge_later
  end

  private

    def set_attachment
      @attachment = ActiveStorage::Attachment.find(params[:id])
      @record = @attachment.record
    end

    def authorize_attachment_parent!
      authorize! :manage, @record
    end
end

Also be sure to wire this new controller up in your routes:

# config/routes.rb
scope :active_storage, module: :active_storage, as: :active_storage do
  resources :attachments, only: [:destroy]
end

It’s also important to update the user interface to remove any reference to the attachment, and in this case I used Rails UJS. The javascript necessary to remove the element from my UI is simple and straightforward:

// app/views/active_storage/attachments/destroy.js.erb
document.getElementById("<%= dom_id @attachment %>").remove()

The only assumptions made here are that you had the representation of your attachment wrapped in a div with ID of attachment_#{id}, which you can easily render using Rails handy dom_id method.

You can now use the following markup next all of your attachments to allow for easy deletion:

link_to "Delete", active_storage_attachment_path(attachment),
         method: :delete, remote: :true,
         data: { confirm: "Are you sure you wanna this?" }

Wrapping it Up

This approach is relatively simple, and relies heavily on the tools and features that Rails provides to us.

In my case I’m also leveraging a common partial to render thumbnails, metadata, and links for attachments, making it dead simple to add attachments to any model I choose to in the future.

As always things evolve over time, and while I’d like ot think I’m perfect I’m sure there are ways to improve this code, so if you have any suggestions or improvements I’d love to hear them.

Migrating Your Assets from Paperclip to ActiveStorage

April, 26 2018 -

With the release of Rails 5.2 there is now a native, built in way to handle asset uploads and management called ActiveStorage, making the need to use gems like Paperclip, Carrierwave, or Fog, with Paperclip going so far as writing a migration guide; implying that it may not be long for this world.

To be honest I didn’t even know that Paperclip had written a migration guide until I started writing this post, and while it’s fairly comprehensive, it doesn’t do what in my mind is one of the most important tasks - migrating the files on the remote host.

With that in mind I’ve used the following rake tasks with great success to move assets for many of my models.

There are two flavors here, migrating assets while changing their name, or just plain old moving.

Moving an Asset While Changing Its Name

In my case I had a User class with a Paperclip attachment called headshot. I never really liked that we used the term headshot, so this was the perfect opportunity to change it to avatar.

This scenario is surprisingly easier due and the fact that we don’t have to worry about a naming collision (headshot to avatar), which means we can still use paperclip’s built in URL helpers in our rake task.

Just remember to leave your Paperclip declarations on your model until after you run your rake task.

Moving an Asset As-is

In reality, you’ll likely want to keep the same name, which requires us to get a little more crafty. In my case I have an Organization class with a logo. We wanted to keep using that term, but in order to do so, we have to remove the Paperclip declarations from the model, otherwise would couldn’t refer to all the new ActiveStorage goodness.

To get around this we just need to utilize the actual asset URL in our rake task.

This rake task is pretty straightforward, but what got a little sticky was dealing with filename extensions, mainly the fact that sometimes uploaded files don’t have them, so that’s what lines 5 & 6 are about.

A Few Considerations

There are a few tradeoffs that you’ll have to consider if you use this method though.

The first advantage that you get is that these tasks can be ran from any server, including your dev environment, even before you deploy any actual code.

In my case I ran the rake tasks on my laptop, and as soon as the updated code was deployed to the server, the assets were already there. The only thing left to do is to remove the old assets when you’re ready, but they are still there in the event you need to rollback your deploy.

The biggest downside to this method is simply the fact that you are duplicating all of your assets temporarily. In my app I was able to move thousands of image files in a matter of hours, but if you have millions of records, or very large assets, this method might not be your best option.

Bootstrap 3 Responsive Button Groups

August, 12 2015 -

Ever been disappointed/pissed off with how Bootstrap handles (read ignores) button formatting on smaller screens? Check this out:

Makes this:

Look like this:

Aaron Draplin Takes On a Logo Design Challenge

December, 11 2014 -

I come across this video yesterday of Aaron Draplin, of Field Notes and DDC fame, talking about his approach to logo/corporate identity design. I’m a big fan of his style in general but he makes some really good points regarding how to approach these types of projects.

Submarine Sandwich

December, 10 2014 -

This video is just flat out weird, but I can’t stop watching it.

Rails Testing

November, 10 2014 -

Below is a series of tweets that I should’ve composed a quick post out of rather than rapid-firing it out over twitter. Maybe I’ll come up with some more to say about this here before long.

When Using CurrentC, You're the Product

October, 29 2014 -

I’ve only used  Pay a few times over the past two weeks, but I already find myself checking for it at every store I step foot into. It’s more convenient than getting into my wallet and swiping a card… and it’s just plain cool.

While the coolness factor will wear off over time, knowing the security measures being taken to ensure my financial/personal details are kept private is a huge win, even more so than the impending EMV roll-out (finally) happening here in the US over the next few years.

All that said, this week’s push-back really had me scratching my head at first. We first heard about Rite-Aid pulling all NFC-based payments from their stores, and CVS quickly follow suit, doing this in favor of a new service called CurrentC.

CurrentC is a new invite-only payment app created members of MCX, which is a group of merchants who are trying to “streamline the customer shopping experience across all major retail verticals.”1

Josh Constine’s article on TechCrunch does a great job describing the basics:

When it’s time for a user to check out, they request to pay with CurrentC. The consumer then unlocks their phone, opens the CurrentC app, opens the code scanner, and scans the QR code shown on the cashier’s screen. In some case, the reverse may happen where the consumer’s CurrentC app displays a payment code and the cashier scans it. If a QR code can’t be generated, a manually entered numeric code may be offered.

I can’t see how unlocking my phone, launching an app, and taking a picture of a QR code could even come close to the level of ease that  Pay, or any other NFC-based payment system, offers.

Rather than sending the customer’s financial data over the air, transactions trigger the transmission of a token placeholder. This is then securely converted by the financial institution to process the ACH payment and charge the user.

CurrentC notes it may share info with your device maker, app store, or developer tool makers. Oddly, it will collect health data. Precise location information is used to verify you’re at the retailer where you’re making a transaction, and if you opt in it can be used for marketing or advertising. CurrentC notes that you can opt in to be able to capture and store photos in the app for a hypothetical visual shopping list or other features down the road.

These quotes tell you all you need to know about MCX’s motives. This decision was made with the merchants’ best interests in mind, not the consumer, they get more personal data about their customers and cut out the transaction fees from the payment processors by using ACH payments. Many people, including myself2, actually prefer credit cards over debit cards/bank transfers. Points, cash-back, and travel credits are big business for banks, and can be beneficial to consumers too, so I don’t expect this to be ironed out any time too soon.

If you’re as interested in this as I am you may want to follow Nick Arnott’s coverage/commentary on iMore and twitter. He’s doing a great job of explaining the implications from both a consumer-facing and technological level.

  1. Read in @marcoarment’s ‘BRANNNNNDS’ voice 

  2. Costco AMEX FTW 

Fire Talk with Me

August, 29 2014 -

I recently started listening to a newer podcast on esn.fm called Fire Talk with Me. It’s a limited edition show that runs through each episode of David Lynch’s masterpiece Twin Peaks, and gives the perspectives of one person who experienced the show during its inital run, Jeremy Smith, and one who’s experiencing it for the first time, Allie Goertz. They have guests on along the way, but are very good about not spoiling anything for Allie. They have currently covered the pilot and the first four episodes, releasing a new one each week.

Whether you are a total Lynch fanatic, or know nothing about Twin Peaks, this is a great podcast to supplement your viewing experience, which can stream in its entirety on Netflix, or splurge for the Blu-Ray set, complete with the prequel movie, Fire Walk with Me.

Either way I can’t recommend you watch this enough. It gives you a lot of perspective as to what shaped some of the ideas/styles of some of today’s best television shows.

The Walking Dead: The Game - Season 2

December, 18 2013 -

The first season if this game was fantastic, and I highly recommend it, even if you aren’t particularly a fan of the TV show. These games are set in the same universe but follow an entirely seperate set of characters… with the occasional cameo :)

The Dirty Secret Behind the Salesforce $1M Hackathon

November, 24 2013 -

I know that if I put this much time and effort into something and the judges didn’t even launch my code, I’d be pretty upset, but anyone going into this thinking it was completely unbiased, and not a huge marketing ploy cooked up to benefit the host is just naive. On the other hand, it’s impossible to get the one true story in situations like these.

At any rate this is why I’m increasingly dismissive of any corporate “hackathon” competitions. They are rarely setup with the developers’ best interests in mind, but rather for some other type of self-serving agenda1.

  1. “I’m not lumping any type of charity/non-profit events in here, those clearly articulate who these projects benefit” 


© 2018 Kyle Keesling — Built using Jekyll w/ source available on GitHub