Skip to content

attr_accessible with many roles

I recently ran into an interesting issue with attr_accessible due to overlooking certain configurations I made in my application.

The symptom was that when I would add attr_accessible to one of my models, suddenly all of the attributes would be inaccessible.

The problem stems from the fact that I had configured mass_assignment_role (used by attr_accessible) to resolve to :admin automatically if I was logged in as an administrator.

class ActiveRecord::Base
  def mass_assignment_role
    role = mass_assignment_options[:as] || (Authorization.current_user.role_symbols.include?(:admin) ? :admin : :default)
  end
end

The problem is that by defining attr_accessible for the :default role it automatically locks down all attributes for any other role.

The solution was to simple remember to define the accessibility for both roles whenever defining attr_accessible:

class MyModel < ActiveRecord::Base
  @@accessible_attributes = %(attr1 attr2 attr3)
  attr_accessible *@@accessible_attributes
  attr_accessible *@@accessible_attributes, as: :admin
end

ActiveAdmin, Spork, and the infamous “undefined local variable or method `view_factory'”

If you are using ActiveAdmin and you are also using Spork as part of your testing suite you are probably extremely frustrated with an error that looks something like this:

ActionView::Template::Error: undefined local variable or method `view_factory' for #<#Class:0x000001070ccf80:0x000001070c7648>

This issue has been queued and is discussed further here: https://github.com/gregbell/active_admin/issues/918

Your solution is simple.  Just add the following to your test/test_helper.rb:

Spork.each_run do
  ActionView::Template.register_template_handler :arb, lambda { |template|
    "self.class.send :include, Arbre::Builder; @_helpers = self; self.extend ActiveAdmin::ViewHelpers; @__current_dom_element__ = Arbre::Context.new(assigns, self); begin; #{template.source}; end; current_dom_context"
  }
end

Adding Custom Buttons To Active Admin

While there doesn’t appear to be explicit support for managing the buttons that appear in the top right of active admin (“New Share”, “Delete”, etc.) I did some digging and managed to find a solution.

ActiveAdmin.register Share do

 action_item only:[:show] do
   link_to "Message Customers", new_admin_share_blast_path(share_blast:{share_id:share})
 end

end

Calling the action_item method will print the contents of the block as a button in the area in question.  You can control which pages you would like the custom action item to be displayed on by passing a hash with either an :except or :only key.

Installing ruby with rvm and homebrew

When I tried install ruby 1.9.3 I kept getting this error:

It seems your ruby installation is missing psych (for YAML output)

I checked my installations and found that I had libyaml located in /usr/local/Cellar/ courtesy of homebrew.  The solution was to tell RVM where to find libyaml during install:

rvm install ruby-1.9.3-p0 --with-libyaml-dir=/usr/local/Cellar

Why doesn’t Stringex work with Mocha?

If you are getting routing issues with models using acts_as_url from Stringex, but on in your tests, and only tests that use Mocha’s stubbing methods, this is for you.

Stringex does all of its acts_as_url crunching in before_validation callbacks.  If you are stubbing the :valid? method, that is your problem.  Mocha’s stubbing method actually rewrites the method being stubbed, therefore, unless you stub the :valid? method to actually call it’s callbacks you will no longer get all the handy url setting done by Stringex.

Overriding Load Methods for Declarative Authorization

The trick here is to be sure that the methods you are overriding are declared specifically as protected methods.  If they are public or private methods, Declarative Authorization will ignore them.

So, in your UsersController, declare it like so:

protected
def load_user
  @user = User.find_by_login(params[:id])
  raise ActiveRecord::RecordNotFound.new("Could not find User with login=#{params[:id]}") unless @user
end

If you do not explicitly raise the error in this method when the record is not found, DA will fall back to it’s default loading methods.

Testing with Spork, Guard, and Test::Unit

Gemfile:

group :test do
 gem 'factory_girl_rails'
 gem 'mocha'
 gem 'capybara'
 gem 'spork', '~> 0.9.0.rc'
 gem 'spork-testunit'
 gem 'guard-spork'
 gem 'guard-test'
 gem 'rb-inotify', :require => false
 gem 'rb-fsevent', :require => false
 gem 'rb-fchange', :require => false
 gem 'growl_notify'
end

Run:

bundle install
spork --bootstrap
guard init spork
guard init test

Guardfile:

guard 'spork', :wait => 60, :test_unit_env => { 'RAILS_ENV' => 'test' } do
 watch('config/application.rb')
 watch('config/environment.rb')
 watch(%r{^config/environments/.+\.rb$})
 watch(%r{^config/initializers/.+\.rb$})
 watch('Gemfile')
 watch('Gemfile.lock')
 watch('test/test_helper.rb') { :test_unit }
end

guard :test, :drb => true do
 watch(%r{^lib/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
 watch(%r{^test/.+_test\.rb$})
 watch('test/test_helper.rb') { "test" }
 # Rails example
 watch(%r{^app/models/(.+)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
 watch(%r{^app/controllers/(.+)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
 watch(%r{^app/views/.+\.rb$}) { "test/integration" }
 watch('app/controllers/application_controller.rb') { ["test/functional", "test/integration"] }
end

test/test_helper.rb:

require 'rubygems'
require 'spork'
Spork.prefork do
 # Loading more in this block will cause your tests to run faster. However,
 # if you change any configuration or code from libraries loaded here, you'll
 # need to restart spork for it take effect.

 ENV["RAILS_ENV"] = "test"
 require File.expand_path('../../config/environment', __FILE__)
 require 'rails/test_help'
 require 'mocha'

 # =====================
 # = Integration Tests =
 # =====================
 require "capybara/rails"
 module ActionController
  class IntegrationTest
   include Capybara::DSL
  end
 end

 # ===============
 # = Other Tests =
 # ===============
 class ActiveSupport::TestCase
  fixtures :all
 end
end
Spork.each_run do
 # This code will be run each time you run your specs.
end

If you are using Active Admin you are going to want to include a little patch in your each_run block to avoid getting an “undefined method: view_factory” error:

Spork.each_run do
  ActionView::Template.register_template_handler :arb, lambda { |template|
    "self.class.send :include, Arbre::Builder; @_helpers = self; self.extend ActiveAdmin::ViewHelpers; @__current_dom_element__ = Arbre::Context.new(assigns, self); begin; #{template.source}; end; current_dom_context"
  }
end

Run your tests:

guard
Follow

Get every new post delivered to your Inbox.