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
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
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
nly key.
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
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.
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.
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