Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Rails 3.x gem for moderating ActiveRecord models (like posts, attributes...).
Ruby Perl

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.



Build Status


Add it to your project's Gemfile

gem "has_moderated", ">=1.0.rc9"

and run

bundle install

To set up has_moderated in your project, use

rails generate has_moderated:install

rake db:migrate

This will generate a Moderation model and a migration for it.


When upgrading, rerun the generator

rails generate has_moderated:install

If you are upgrading from a version lower than 1.0.x you need to remove the old create_moderations migration file.

Make sure you have reviewed and accepted/declined all current moderations before upgrading! This means the moderations table in database should be empty, otherwise the migration process might fail (on purpose).

Why use this instead of something like Papertrail?

Papertrail and vestal_versions for example, are designed not for moderation, but for tracking changes on models. If you use Papertrail for moderating, you will always have the “newest” version in the database, while previous changes will be recorded in a special table. The problem with this for moderation is that when you are showing records, you have to read from both the model table and the “changes” table in the database, and do additional processing to combine them. This will impact performance quite a bit.

In contrast, with this gem the database will always represent the last accepted/confirmed version of the model, and not necessarily the newest as with Papertrail. A “changes” table like in Papertrail is used here too, but it is used to store “suggested” (to-be-accepted) changes. Therefore you have no performance hit when querying for the last accepted version of the model, like you would have with Papertrail. You only have a performance hit when accepting a moderation, but this is irrelevant. Also this way all changes submitted are relative to the currently last accepted version and not to the newest version like in Papertrail.

So Papertrail and vestal_versions are designed for model versioning and changes tracking, while this gem is designed for moderation. The database will not be changed until a moderation is actually accepted.

Note that because of this, handling associations is quite a bit harder, so this gem is a little bit more complex. Therefore is recommended you test moderated associations before deploying to production, to avoid any possible bugs.


To moderate one or more attributes, add

has_moderated :attr1, :attr2

to your model.

If you want to moderate the creation of a record (for example, you don't want new blog posts to show up until they are accepted by a moderator), use


You can also specify associations that need to be saved for moderation as well (if moderating the creation of new records) - for example, if a Post has_many :links, and you want to submit these links to moderation as well (note, if you don't, they will be discarded), use

has_moderated_create :with_associations => [:links]

in your Post model (post.rb). This only matters when you use has_moderated_create. You can also use

has_moderated_create :with_associations => :all

to include all associations, but I recommend you explicitly specify them if possible. By default, no associations are included.

To moderate destruction of records, use


Warning! Always put has_many etc. BEFORE has_moderated calls in your model!

Moderating associations

To moderate associations use:

has_moderated_association :links, :comments

to moderate the links and comments associations. You can use :all to moderate all associations, but I recommend you explicitly specify them.

Be especially careful if you are moderating a has_many :through association. In this case, you must moderate both associations. For example:

has_many :bookshelves
has_many :books, :through => :bookshelves
has_moderated_association :bookshelves, :books

Manage moderations

To see pending moderations, simply call


You can also see moderations for a specific record. For example, if you have Post model, you can call moderations on it.

post = Post.first

Moderation is a normal ActiveRecord model, you can inspect it in rails console to see what it holds. Data is serialized in YAML format

The data can be deserialized using YAML::load, but this has already been done for you


To accept a moderation, call


to discard (destroy) it, call


Do not use moderation.destroy, because discard triggers certain callbacks which may be necessary.

Preview moderation

You can see a preview of what a moderation will do.

Get a read-only preview

You can get a read-only preview of the actual ActiveRecord object. It allows you to see the value of all attributes, as well as all the associations (including non-moderated, except the moderations association). Since it is an ActiveRecord object, it also supports dirty tracking ( Note that association methods (preview.some_association) are not real ActiveRecord relations, but just Arrays. If you really need real relations, look at live_preview below.

preview = moderation.preview
preview.some_attr # => "new value"
preview.some_attr_change # => ["old value", "new value"]
preview.some_association.first # => AR object

This preview is cached from applying the moderation in a transaction and then rolling it back (so it does not affect the database). Note if you have custom model methods that return other ActiveRecord records, those records will not be frozen, so if you save those changes WILL be written to the database.

Using the preview to modify the moderation

The preview described above has a nifty feature (1.0.rc7+) that allows you to update attributes in the moderation by manipulating the fake object. Right now it can only handle attributes and not associations, but that might change in the future. See example below on how to use this feature.

moderation.parsed_data # => {:attributes=>{"title"=>"Task 1"}}
preview = moderation.preview(:saveable => true)
preview.title = "Task 2"
moderation.parsed_data # => {:attributes=>{"title"=>"Task 2"}}
Task.first.title # => "Task 2"

Get a live ActiveRecord preview

Usually you don't need to use this. The difference compared to the above method is that the above will freeze objects from the database and then roll back the transaction, and you will get the frozen copies that you can use at will. Those copies do have fake association methods that just return Arrays instead of AR relations.

In contrast to preview, live_preview will let you use the record while the transaction is still going on, so it is not even frozen. Also the association methods are real ActiveRecord relations in this case. After your block completes, the transaction will be rolled back and you will not have access to the preview anymore (and you should not use it outside of the block).

moderation.live_preview do |preview|
  preview.some_attr # => "new value"
  # note how we need to use .previous_changes when using live_preview,
  # .changes will be blank as the record has already been saved to DB
  preview.previous_changes # => {"attr"=>["old", "new"]}
  preview.some_association.first # => AR object

Note that since a transaction is used in both cases, all the changes made to the database to produce the preview are only in effect inside the transaction, so the changes are only visible inside the transaction. For example, if a second database query comes from another Rails instance, it will not see the changes that are in effect in the transaction, even if the transaction is still in process.

Example of using preview in your show action

In this example I allow admin users to add a preview parameter to the URL to see the preview of how the record will look after applying a moderation, without having to change code in the views.

def show
  @doctor = Doctor.find_by_slug(params[:id])

  if current_admin_user && params[:preview]
    @doctor.moderations.find_by_id(params[:preview]).live_preview do |preview|
      @doctor = preview
      render :action => "show"
    render :action => "show"

Which kind of moderation is it?

You can call the following convenience methods to determine the type of moderation


Bypassing moderation

To bypass moderation (e.g. for admin users), wrap your code into without_moderation like so:

record.without_moderation(current_user.is_admin?) do

If the parameter is false, then it will have no effect. If the parameter is true, then moderations will be disabled for code inside the passed block. The parameter is optional and defaults to true.

Special data attached to moderations

If you need any special data attached to the Moderation model, you can use the moderation_creating hook.

Attaching user ID to moderations

For example you have a Comment model, and it is moderated. But your visitors are logged in when they post comments, so you want to add the user ID to the moderation.

  • first create a migration to add user_id into Moderation

    rails g migration AddUserIdToModerations user_id:integer
  • run rake db:migrate

  • in models/comment.rb, add something like this:

    attr_accessor :moderation_user_id
    moderation_creating do |m|
      m.user_id = self.moderation_user_id

This is just one example how you can do it. You need the attr_accessor here because we are going to pass the user ID from the controller. In the hook you have access to the Moderation model just before it is saved, so you can modify it like any other model. Now just set moderation_user_id on the model before you save it:

c =
c.moderation_user_id =

CarrierWave support

There is support for CarrierWave uploads to be moderated. If you need some more customization look at this module in lib/has_moderated/carrier_wave.rb and make your own version.

Example of moderating uploads

This will moderate the upload no matter if the record is being created, or updated. Other unmoderated fields will be saved normally.

mount_uploader :avatar, GenericUploader
include HasModerated::CarrierWave
has_moderated_carrierwave_field :avatar
has_moderated :avatar

Example of moderating create of the whole model using a CarrierWave uploader

This will moderate creating the whole record (all its fields), including the upload.

mount_uploader :avatar, GenericUploader
include HasModerated::CarrierWave
has_moderated_carrierwave_field :avatar

To moderate later changes to the upload (avatar), additionally add

has_moderated :avatar

Example of moderating on association

If you are moderating some other model, and the model with the upload is an association to it, you still need to add this

mount_uploader :avatar, GenericUploader
include HasModerated::CarrierWave
has_moderated_carrierwave_field :avatar

This will only moderate the upload when it's being saved through an association which has

has_moderated_association :photos


I've tested this project using RSpec. You can find the tests in


You can run the tests by running

rake spec

in the root directory of this gem (so you have to clone it first).


If you have problems open an issue here on Github.

You may encounter problems with models that have some sort of non-serializable attributes. This might be something like file attachments, you'll have to try it to see. If you have a problem like that you can extract the problematic attributes into a seperate has_one association. If you moderate create, save that model without the foreign key first, and then use has_moderated_create :with_associations => [:association_name] and add the association (to the existing associated model) before saving the moderated model. If you have questions about this or don't understand what I mean, open an issue here at GitHub and I will explain it further.

It is also possible you will encounter problems with some heavily customized associations, or with has_many :through associations (although I have tested this for a simple, ordinary case and it works). If that happens open an issue here on Github.

My personal TODO (ignore this)

This is just for my personal todo list…

perhaps can use .freeze for preview?

improve functionality when rerunning has_moderated:install generator

hm maybe has_moderated_association could be just a has_moderated_create on the assoc? just check stuff like if task_id is not nil or something. could be much easier to implement then

also maybe use dirty

and provide the same interface to show changes before accepting a moderation (just overwrite the dirty methods on the instance, also overwrite save at a low level to throw exception since its only a preview)

to make it possible to support other than activerecord, use activemodel when its possible AND try to make it so its possible to call the last step before activerecord (e.g. a func that reads all the values of current attributes and figures out which ones changed) - and use this in activerecord.

use YARD for docs check again railscasts episodes for gems (e.g. railties) Preview method which gives changed object but doesnt save it.


This project rocks and uses MIT-LICENSE.

Something went wrong with that request. Please try again.