Testing Mulitenancy with Minitest and Capybara

Lately I have used an outside in approach to building my web application in ruby on rails.  What that means is that I am driving my application development by building integration tests.  I picked up this technique a year or so ago from thoughtbot.  So far, i have really enjoyed the insight it has given me and how much less code I write than when i was doing things by building models first.

Many of the applications I end up building as a consultant are multi-tenant application.  So there are many customers that have to live in strict or limited scope from each other.  I have built a really simple helper that I have been using and wanted to share it below in my TestCase class description


class ActiveSupport::TestCaseActiveRecord::Migration.check_pending!
  include Warden::Test::Helpers
  fixtures :all

  def as_user(user)
    login_as users(user.to_sym)
    yield
    logout
  end
end

 

It is super simple and I was a little hesitant to put it out there.  All it does is a login the user, execute the block that is passed in and then log the user out.  In a multitenant app though it is really imporant to check your scopes based on the type of user that is logging in (admin, super admin, org admin, user, anonymous, etc).  On top of that it adds to some very understandable tests.  Ozark and nixa are just two random cities in my area.

 


scenario "Employee cannot visit other organizations tickets" do
  as_user(:ozark_employee) do
    ozark_ticket = organizations(:ozark).tickets.first
    visit ticket_path(ozark_ticket)

    page.text.must_include unauthorized_error
  end
end

scenario "Can edit own tickets" do
  as_user(:ozark_employee) do
    visit tickets_path
    click_link 'Edit'
    fill_in 'Description', :with => 'Some new description xxx'
    click_button 'Update Ticket'

    page.text.must_include 'successfully updated'
    page.text.must_include 'Some new description xxx'
  end
end

This one simple helper has really tied in with my multitenant outside in development and sped up my dev time when building out how different types of users should experience an application.

Script to generate fixtures from a database

So I have used minitest for about 8 months now for testing and all in all it turns out that I loved it more than I expected.  I just moved back to an in progress in house project written using Rspec with FactoryGirl and realized I want my minitest and  fixtures back.  So i found a great script that will generate fixturess based on my database seed.

 

sql = "SELECT * FROM %s"
skip_tables = ["schema_info"]
ActiveRecord::Base.establish_connection
ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
  i = "000"
  File.open("#{Rails.root}/test/fixtures/#{table_name}.yml", 'w') do |file|
  data = ActiveRecord::Base.connection.select_all(sql % table_name)
    file.write data.inject({}) { |hash, record|
      hash["#{table_name}_#{i.succ!}"] = record
      hash
    }.to_yaml
  end
end

Redirecting To Dynamic Page Using Javascript

So I recently had a consulting opportunity where the client needed to be sent to a dynamic page after a controller action in rails.  That is easy in the normal rails context, however this all had to be done in a javascript content due to it being a javascript based subscription form for sign up.  I ended up with a nice little parlor trick that is attached below.

A little background, this client used recurly to do recurring billing and recurly uses a javascript system to securely handle creditcard data.  On submit, javascript sends recurly the credit card data and they send you back a token representing the data in their system or you get back errors that you can display to the browser.

Once you have the token there are a couple ways you can approach charging it to add a subscription.  The way that I do it is by setting up a subscription/order controller in rails, that then takes care of creating the subscription through an Ajax request. That allows us to catch any errors (i.e. not enough funds) without forcing the user to re-enter any non-credit card data on the site.

Normally after a successful subscription, you will let the form continue to a subscription/registrations controller and that will take care of the setup.

In this app, there were multiple paths that the user could go based on what they order and their progress in the system. So i needed to redirect them dynamically. The way that i did that is on success instead of allowing the form to continue using javascript, i called the function below.

function redirectOrder(url){
    var plan = $('#recurly_plan_code').val();
    var form = $('<form action="' + url + '" method="get">' +
      '<input type="text" name="recurly_plan_code" value="' + plan + '" />' +
      '</form>');
    $('body').append(form);
    form.submit();
  }

Setup Phonegap on Ubuntu 14.04

Worlds succinctest guide to setting up a phone gap project.

  • -> npm install -g phonegap
  • -> npm install -g ripple-emulator
  • MAKE A PROJECT
  • CD to the project
  • -> phonegap build browser
  • ripple emulate

Then open a browser

http://localhost:4400/platforms/browser/www/index.html?enableripple=cordova-3.0.0

Thats it!  Your cordova version may be different, the important part is the URI, which is the path between / and ?

Pundit and Rolify Testing with Rails

Just ran into an oddity that I didn’t expect (due to a false assumptions) when testing permissions and wanted to send it out to the world.

I have an app with two types of roles; :admin or :user. I am using Rolify in combination with Pundit to perform the role based authorization of REST actions.

I mixin a module containing general purpose authorization classes. I do it this way because permissions schemes tend to change alot and centralization makes it easier to change. You could easily just add these to your pundit policy classes.

def is_admin?
  user.has_role?(:admin)
end

def is_user?
  user.has_role?(:user)
end

def is_allowed?
  user.has_role?(:user, record)
end

Pretty straight-forward. An administrator has the role of :admin, a user has a role of :user and a :user is only allowed to access records that they are explicitly granted permission on.

My error was the assumption that user.has_role?(:user) would return true if a user had a role of :user explicitly set on any object in the system. In a pundit action it would look something like this.

def index?
  is_admin? || is_user?
end

The problem is that this was returning false unless someone had a :user role set in general, not on a specific instance of a record.

I think code clears this up further, here is the error.

#Assign the user role of :user on a specific object instance
User.first.add_role(:user, SomeObject.first)
User.first.has_role?(:user) #= false

#Assign the user a role of :user
User.first.add_role(:user)
User.first.has_role?(:user) #= true

So i had to alter this to use see if the User had the role of :user on any object in the system.

#Old definition, broken
def is_user?
  user.has_role?(:user)
end

#New definition, fixed!
def is_user?
  SomeObject.find_roles(:user, user).any?
end

The new is_user? returns true when the user has a role of :user on any SomeObject in the system. Which is exactly what we need.

Lesson learned, I assumed that the method worked a certain way and lost several hours of debugging in my unit tests. I normally will go straight to IRB when i run into anomalies but because i strongly help my assumption I didn’t.

Rails 4.2 truncate_words method

Rails 4.2 added a new feature called truncate words. It allows you to do things like this.

"In a world where everything is awesome".truncate_words(3)
"In a world..."

I wanted to step through this method to look at a few things I found in the code. Here is the full code

def truncate_words(words_count, options = {})
  sep = options[:separator] || /\s+/
  sep = Regexp.escape(sep.to_s) unless Regexp === sep
  if self =~ /\A((?:.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
    $1 + (options[:omission] || '...')
  else
    dup
  end
end

The first thing that I noticed is that it recognizes two additional options
:separator allows you to change the thing that splits up words. For instance, you could do something like cut off the string at the third i. That would look like this.

"Once i went to india and ate ice".truncate_words(3, {separator: 'i'})
"Once i went to india and ate ..."

There is also :omission, which allows you to add something besides … at the end of the truncation.

Now that we know the options, lets look at the code.

The first two lines setup the separator that is used to count the number of words. The first line either takes it from the options hash passed in or defaults it to a space. The second line converts the separator into regular expression language by escaping it (i.e. adding 1 or more \) UNLESS the separator is already of class Regexp. I suspect the unless is so that we don’t get double escaping that isn’t handled already in the Regexp library.

sep = options[:separator] || /\s+/
sep = Regexp.escape(sep.to_s) unless Regexp === sep

Now that we have our separator Regex’ed out, we check the string for it’s match. Self is going to be some sort of string that responds to matching a regex. If it finds a match the truncate_words will return the match (which is stored in the $1 global variable) along with either the specified omission or ‘…’ at the end. If we don’t get enough matches, then the string is just duplicated out.

if self =~ /\A((?:.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
  $1 + (options[:omission] || '...')
else
  dup
end

Something I’m wondering about is why do they duplicate the string at the end if no match is found? Wouldn’t just sending out self work and not eat up a tiny bit more memory?

In my next post I analyze the regex that is used to do matching on this string.

Thoughts on my first EmberJS project

Just cooked up an EmberJS project and wanted to share some thoughts on the framework as a whole and also throw some gotchas.

1. Many things work out of the box. However, when you need to do something that is not out of the box, you will find few resources to help. Worse yet, things change so much that found resources may not.

2. Some things are shockingly easy to do. Some of those things include data validations, 2 way binding and active record (rails) integration with active model serializer.

3. Some things are shockingly hard to do. For instance, looking over a javascript object is pretty simple in javascript or jQuery. However, in ember (with handlebars) you have to convert it into an ARRAY first, then it will loop pretty easily. This surprised me because EmberJS is javascript… looping over and displaying JSON should be pretty easy.

If I had one recommendation, it is to not use templating languages or extensions like emblem. I used this during my first project. It is similar to slim or haml and makes it super easy to see how the markup will be. But the downside is that whenever you need to do something unique, you will fight with it in order to get it implemented.

Javascript frameworks look like they will be the way of the future so good luck if you get started!

Adding Nested Image Attributes to Rails 4 App using Paperclip

So have a new web application where users will be able to upload images along with a post. I haven’t used the ruby gem Paperclip from ThoughtBot since rails 3.2 (which seems like it was only a couple months ago) so I thought I would write up a post on how I got it working.

The background of the app

  • Post have many images
  • Image is polymophic and can belong to either Post or Comment

So first step is to add the gem ‘paperclip’ to your gem file and then run bundle install.

Next, configure paperclip in development/production.rb   For this app we will be doing file storage locally until it is live

config/enviornments/development.rb

Paperclip.options[:command_path] = "/usr/local/bin"

After that we need to setup our models for Image and Post. Image is polymorphically related to Post as imageable and also contains content through the paperclip gem. Post has images and can accept nested attributes for images.

app/models/image.rb

class Image < ActiveRecord::Base
  belongs_to :imageable, :polymorphic => true
  has_attached_file :content, :styles=>{:medium => "300x300>", :thumb => "100x100>"}
  validates_attachment_content_type :content, :content_type => %w(image/jpeg image/jpg image/png)
end

app/models/post.rb

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :images, :as => :imageable, dependent: :destroy
  accepts_nested_attributes_for :images
end

Now that the models are good, we have to code around the strong parameters that were added in rails 4. Notice that in the private section we are adding ‘images_attributes: [:content]’ to prevent the image upload from being filtered and adding @post.images.build to the new action so that our view can render out the file tag properly.

app/controllers/posts_controller.rb

class PostsController < ApplicationController
  
  def new
    @post = Post.new
    @post.images.build
  end  

  def create
    @post = Post.new(post_params)
    @post.user_id = current_user.id

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render action: 'show', status: :created, location: @post }
      else
        format.html { render action: 'new' }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    #strong parameters
    def post_params
      params.require(:post).permit(:title, :description, images_attributes: [:content])
    end
end

Last part is the view, now that it is wired up it will just be adding a couple lines to our form. I removed the code that will spit out the errors that come through for succinctness here. Note that the upload image part has :images in fields_for which corresponds to our has_many :images in Post model and that the file_file :content corresponds to the has_attached_file :content in our Image mode.

Also, in order for the html field to accept images, make sure you add the multipart to the html.

app/views/posts/_form.html.erb

<%= form_for @post, :html => { :multipart => true } do |f| %>
  <div class="field">
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :description %><br>
    <%= f.text_field :description %>
  </div>
  <div>
    Upload Image
    <%= f.fields_for :images do |ph| %>
      <%= ph.file_field :content %>
    <% end%>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

That about does it. If you put this together and it doesn’t work, restart your server and it should start singing.

Exporting Data to CSV in Ruby on Rails

So in our application we have a list of clients that our users are tracking (called trackers). One of our clients needed the ability to quickly export this data. In ruby this is super easy. We just add a few lines to our controller and model. In our controller, we already have an instance of their active trackers (@trackers_enabled )and it just grabs the trackers that are enabled.

trackers_controller.rb

respond_to do |format|
  format.html
  format.csv { send_data @trackers_enabled.to_csv}
end

This allows us to export the data by just appending .csv to the url, so to access this we just add a link in the view. In the model we just add.

tracker.rb

  
def self.to_csv(options = {})
  CSV.generate(options) do |csv|
    csv << column_names
    all.each do |row|
      csv << row.attributes.values_at(*column_names)
    end
  end
end

This is a quick solution and can be found in about 20 places around the internet, but it adds some unnecessary fields like created_at, updated_at and user_id to the csv.

To limit that data we just have to do some investigation on the column_names and see what type of data it returns in IRB.

In irb

Tracker.column_names
=>  ["id", "user_id", "address", "first_name", "last_name", "enabled", "created_at", "updated_at"]

So now we know that column names is just an array of the columns. To grab just the ones we are interested in we edit tracker.to_csv to have the columns we want or we could edit column_names to just specify the columns we want. I opted with the first because it won’t have any unintended consequences.

tracker.rb

  
def self.to_csv(options = {})
  CSV.generate(options) do |csv|
    columns = %w(first_name last_name age address description)
    csv << columns
    all.each do |row|
      csv << row.attributes.values_at(*columns)
    end
  end
end

Then in the view code you can add something like this if you are using bootstrap.

<div class="container-fluid padded">
  <%= link_to "Export #{@trackers_enabled.count} Trackers to CSV", params.merge(:format => :csv), :class => "btn btn-green" %>
</div>