Gems

An ActiveRecord Assumption: Using delete_all on a Related Model, StackLevel Too Deep

Recently I've been working with larger datasets than I previously have in the past, which has led me to learn much more about how ActiveRecord handles mysql queries. When using a gem or a library that does a lot of behind the scenes magic, there is a tendency to rely on that magic; until it bites us in the ass. I got myself into such a situation with delete_all on a related model.

I changed a bit of our code in the PhishMe app to use delete_all instead of destroy_all or a method we previously used to destroy in batches. I wanted this process to be faster and since we didn't need the callbacks delete_all would work perfectly.

I expected that the call for Model.related_mode.delete_all to produce the following mysql query:

DELETE FROM `related_model` WHERE `related_model`.`id` = '1'

In fact, the produced mysql that was created by the ActiveRecord query was:

DELETE `model` WHERE `model`.`id` = '1' AND ((`related_model`.`id` = '2'<br />OR `related_model`.`id` = '3' OR `related_model`.`id` = '4' OR<br />`related_model`.`id` = '5' OR...))
 13 

The above query normally wouldn't cause any issue and you might not even noticed the slowed down query, but if you have thousands of records it blows up, and not gracefully.

When you hit the threshold of the number of records that can be chained, ActiveRecord spits out a "StackLevel too deep". (facepalm) After I thought about it, the issue was obvious. The stack really is too deep. I did actually ask mysql to chain all the methods. I didn't expect this behavior because I had never considered the ramifications of the Model.related_model.delete_all call I was making.

I'm becoming very interested in how our ActiveRecord calls translate into mysql queries. It's easy with "magic" gems to ignore the underlying behavior.

Now I've Got Pretty Permalinks and Much More

Note: This blog post referred to an older version of this blog that I had built from scratch. I've attempted to update links so they don't 404, but know that this post is not currently accurate.

I'm off to a pretty good start with my yearly goals. It may not look like it but I've made a lot of changes under the hood to this blog. I was finding my ability to find myself on google pretty low so I added a sitemap — and it's not just any sitemap, it auto-updates using sweepers. In the next post I'll go over how to build the sitemap specifically.

A couple other new features are "pretty permalinks" and an RSS Feed. For the permalinks I used the Stringex gem. The gem allows you to more easily control your permalinks and comes with a nifty little method that allows you to create the URL's for already existing posts. I then made sure I added rewrite rules to my nginx config so that the old /posts/1 urls still functioned. Nginx rewrite rules are a little different from Apache as you can see in my exmaple below:

rewrite ^/posts/1(/)?$ /posts/hello-world permanent;

Lastly, I updated my 404, 422, and 500 pages to actually look like my site. Yipee!. I should have done that earlier, but when I built eileenbuildswithrails.com I wanted to get it live before the New Year.

Next set of changes will involve adding pages to the cms, including a contact form, ability to add images, and perhaps comments. Thanks for reading!

Categories: tips nifty-methods gems

Lots of Love for ActiveAdmin

I have been so heavily immersed in work projects that I haven't had the time to write posts about rails - or do much of anything else.

Recently, I have been using the ActiveAdmin gem for the websites I've been building with rails. We are rebuilding our company website with rails and an ActiveAdmin backend. ActiveAdmin is awesome, the documentation could use a little love, but other than that I have no complaints about the system. I do believe everyone should build a rails administration system from scratch at least once to ensure understanding of how authentication works.

I'm going to go over some of the basics of getting up and running with ActiveAdmin and then in later posts will go into more detailed instructions on more complicated things you may want the admin to do like model relationships and integration with paperclip.

Installation

To get started add activeadmin and meta_search to your Gemfile. Bundle install and begin development! Activate the default admin user model by generating the resource: rails generate active_admin:resource AdminUser. The default login information is admin@example.com/password.

Beginning Development

First, the admin user model must be configured. An important thing to note on installation is that all the fields for the user are visible including encrypted_password, reset_password_token, etc and if you try to update/add a user before changing the available fields you will get a "mass assignment" error. Find the admin_users.rb in app > admin and add the following:

ActiveAdmin.register AdminUser do
  index do
    column :id
    column :email
    column :full_name do |field|
      "#{field.first_name} #{field.last_name}"
    end
    column :last_sign_in_at
    default_actions
  end

  show do
    attributes_table do
      row :id
      row :full_name do |field|
        "#{field.first_name} #{field.last_name}"
      end
      row :email
      row :last_sign_in_at
      row :sign_in_count
    end
  end

  form do |f|
    f.inputs "Edit User" do
      f.input :first_name
      f.input :last_name
      f.input :email
      f.input :password
      f.input :password_confirmation
    end
    f.buttons
  end

  filter :id
  filter :email
end

You'll notice I've added extra fields to the db, namely a first_name and last_name to create full_name. This file changes the main views in the administration interface — the index view (table of users), the show view (each user's settings), and the form (updating/adding users to the admin panel).

Be sure to update your model to include validations, messages and relationships.

Adding new models is as simple as generating a model and a resource. If you want to create a model that isn't updated through the ActiveAdmin interface, create a has_many join table model (ex, categorizations), but leave out the resource generation.

And that's all you need to get started, very simple gem to use. Although I could get a lot more in-depth about the features of ActiveAdmin I'm going to pause here. I encourage you to play around with it, the ease of use makes development even more fun. It's great to not have to worry about designing your admin interface for fast development.