Running with Ruby

Tag: initializers

Testing Sinatra application methods in isolation with RSpec

Sometimes you may want to test some Sinatra app methods in isolation (outside of a standard request flow). You probably even tried to do something like this:

require 'spec_helper'

describe App do
  subject { App.new }

  it 'expect run some specs here'
    expect(subject.current_user).to eq user
  end
end

Unfortunately you will end up with error like this:

NoMethodError: undefined method `current_user' for #<App app_file="/home/something/app.rb">
from (irb):2
from /home/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

This will occur, because Sinatra’s App new method creates a Sinatra::Wrapper, not an App instance. Of course everything will work as it should, if you follow the get/post/put/delete way of testing Sinatra apps:

require 'spec_helper'

describe App do
  before { get '/api/v1/credit_cards.json' }
    
  it { expect(last_response).to be_ok }
end

but it will fail when trying to invoke any App methods directly.

Luckily there’s a really simple solution to this issue. You just need to initialize it by manually allocating space and “bypassing” the new method:

require 'spec_helper'

describe App do
  subject do
    app = described_class.allocate
    app.send :initialize
    app
  end

  describe '#current_user' do
    before do
      expect(subject)
        .to receive(:params)
        .and_return(params)
        .at_least(:once)
    end

    context 'when there is no user_id' do
      let(:params) { {} }

      it { expect(subject.send :current_user).to be_nil }
    end
  end
end

That way you can get the “real” app instance (not a wrapper) that you can use as a subject in your specs. That way you can test your Sinatra app methods in isolation – without having to call a full request.

Paperclip, Bootstrap and SimpleForm working together on Rails

Few days ago, I’ve decided to get back to the “original” Paperclip. Until now, in one of my projects I’ve been using forked version with additional tweaks. However, supporting it for 3 years was enough. Getting back on track was fairly simple. It took me 1 day to fix the file structure, next day to rewrite validators and that would be all except one thing. Paperclip attaches error to 3 fields (example for thumb file):

  • thumb_file_name
  • thumb_file_size
  • thumb_content_type

I don’t like this idea, since those are basically the internals of Paperclip implementation and in my opinion, all the errors should be attached to the “base” attribute (which in this case is called thumb). Furthermore this is not only the architectural problem but it also affect my views. I use Bootstrap with a SimpleForm attached to it and adding Paperclip to it seams fairly simply:

= simple_form_for @user, :html => { :class => 'form-horizontal' } do |f|
  = f.input :avatar

And… this should be it. However, as I mentioned above, Paperclip attaches errors to fields different than “thumb” (not all of them but it doesn’t matter), so they aren’t displayed on the interface. I could use something like that:

= f.errors :thumb_content_type

and well, this indeed works, unfortunately without any Bootstrap stylings. Also it requires extra line for each attachment that I use. So it sucks! That’s why I’ve decided to do a little hack on it: let’s just copy all the error messages into original “base” attachment name. This should solve our problem (and it did):

# lib/paperclip_extensions.rb
module PaperclipExtensions

  extend ActiveSupport::Concern

  module ClassMethods
    # Changes the default Paperclip behaviour so all the errors from attachments
    # are assigned to an attachment name field instead of 4 different once
    # This allows us to use Bootstrap and SimpleForm without any other extra
    # code
    def has_attached_file(name, options = {})
      # Initialize Paperclip stuff
      super
      # Then create a hookup to rewrite all the errors after validation
      after_validation do
        self.errors[name] ||= []
        %w{file_name file_size content_type updated_at}.each do |field|
          field_errors = self.errors["#{name}_#{field}"]
          next if field_errors.blank?

          self.errors[name] += field_errors
          field_errors.clear
        end
        self.errors[name].flatten!
      end
    end
  end

end

ActiveRecord::Base.send(:include, PaperclipExtensions)

Just put this code into a file in lib/ and create an initializer where you will require it:

# config/initializers/paperclip_extensions.rb
require 'paperclip_extensions'

After that, you are ready to go :) Enjoy using Paperclip with Bootstrap and SimpleForm without any problems!

Copyright © 2018 Running with Ruby

Theme by Anders NorenUp ↑