rake accessify

Written . Tagged Rake, Ruby, Ruby on Rails.

attr_accessible is preferable to attr_protected, but typing out all the attributes to which you want to grant mass-assignment access can be bothersome with big models.

To make this easier, I made a rake accessify task. Run e.g.

1
rake accessify MODEL=user

for output like

1
attr_accessible :age, :location, :location_id, :sex

(but presumably longer). The output is in alphabetical order for your culling pleasure.

Some attributes that are very rarely supposed to be accessible (like *_count and created_at – see code for the full list) are not included. It’s very important to note, though, that the task is only meant to save typing, not to make security decisions. Some few obvious attributes are left out, but you should read through the generated list of attributes very carefully and remove those that should not be available to mass assignment.

To get this, just put accessify.rake in lib/tasks.

The code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
excluded_setters  = %w[id [] attributes]
excluded_suffixes = %w[count ids]

usage = "Specify model like 'rake accessify MODEL=user'."

desc "Outputs an attr_accessible statement with all setters except for those that typically are not for mass assigment, like *_count, *_ids and {created,updated}_{at,on}. #{usage} Note that this task is only meant to save the effort of typing in attributes manually; it does not make any security decisions for you. Read through the generated list of attributes very carefully and remove those that should not be available to mass assignment."
task :accessify => :environment do

  begin
    model = ENV['MODEL'].downcase.classify.constantize
  rescue
    raise "No such model! #{usage}"
  end

  methods = (model.instance_methods - Object.instance_methods).grep(/=$/).map(&:chop)
  columns = model.column_names
  setters = methods | columns
  setters.reject! do |setter|
    setter.match(/_#{ Regexp.union(*excluded_suffixes) }$/) ||
    setter.match(/(created|updated)_(at|on)/) ||
    excluded_setters.include?(setter)
  end
  setters = setters.sort.map(&:to_sym).map(&:inspect).join(', ')
  puts "attr_accessible #{ setters }"

end

Please point out any other attributes that can be discarded by default, and anything else that can be improved. I’m not at all sure this is the best way to pick out all setters.