user419017
user419017

Reputation:

Rails: how to disable persistence of object temporarily?

As in, freeze disables ability to update object values (to a degree). How might I build a method User.disable_persistence that would disable ability to create/save/update on that object and associated objects, both called directly (User.save) and indirectly (User.children << child).

Is there a gem, or a simple method, like:

class User < ...
  def disable_persistence
    # magic here (nullify save, and other methods, prevent callbacks etc.)
    class_eval :before_save do 
      errors.add(:base, "Persistence has been disabled for this object")
    end
  end
end

Upvotes: 3

Views: 815

Answers (4)

Ryan McGeary
Ryan McGeary

Reputation: 239934

Try user.readonly!

>> u = User.last
=> ...
>> u.readonly!
=> true
>> u.save
User is marked as readonly (ActiveRecord::ReadOnlyRecord)

Upvotes: 2

p.matsinopoulos
p.matsinopoulos

Reputation: 7810

The problem seems simple. The tricky part though is the and indirectly (User.children << child) part of it. This can be dealt with easily when the parent object (User) is new record. But not that easily if it is not. This is because a statement like user#children << child saves the parent record and the children when record of user is new but does not do the same when it is not. On latter case it saves only the child. This problem is not solved on this project, automatically, at least for now. Developer has to disable the persistence on the child object first in order to achieve this on this latter case.

See the author_spec.rb file. It is very helpful to tell you the whole story.

The whole project that I developed as task to answer your SOW question is here: https://github.com/pmatsinopoulos/disable_persistence

Anyone that wants to contribute on that, feel free.

The code that does the whole trick is quoted here too for readers convenience:

The disable_persistence.rb file:

module DisablePersistence
  extend ActiveSupport::Concern

  def disable_persistence
    @persistence_disabled = true
  end

  def enable_persistence
    @persistence_disabled = false
  end

  module ClassMethods
    def disable_persistence
      @@class_persistence_disabled = true
    end

    def enable_persistence
      @@class_persistence_disabled = false
    end

    def persistence_disabled?
      @@class_persistence_disabled ||= false
    end

    def persistence_disabled
      persistence_disabled?
    end
  end

  included do
    attr_reader :persistence_disabled
    alias :persistence_disabled? :persistence_disabled

    before_save :can_persist?

    after_initialize do |base|
      base.instance_variable_set(:@persistence_disabled, false)
    end

    def can_persist?
      !persistence_disabled? && !self.class.persistence_disabled?
    end

    protected :can_persist?
  end
end

ActiveRecord::Base.send :include, DisablePersistence

Notes:

A. The instances will be responding to:

  1. #disable_persistence
  2. #enable_persistence
  3. #persistence_disabled?

B. The class will be responding to:

  1. #disable_persistence
  2. #enable_persistence
  3. #persistence_disabled?

C. There is a protected before_save method that checks whether the instance can persist. It check that both instance and class persistence are enabled. If any is disabled, does not allow the instance to persist.

D. The functionality is automatically included in all ActiveRecord::Base classes. This is the last line above. You may not want that. If you do not want that, you have to call include DisablePersistence on all your ActiveRecord::Base classes that you want this feature on.

E. In the rails project that I link to, I have an initializer that requires the file that contains this code. Look into the config/initializers. Otherwise, you will have to require it yourself.

Some examples of usage (Assume an author and their books):

First example:

author = Author.new
author.disable_persistence
author.save # will return false and nothing will be saved
author.enable_persistence
author.save # will return true and author will be saved

Second example:

author = Author.new
author.disable_persistence
book = Book.new
author.books << book
author.save # false and nothing will be saved

Third example:

author = Author.new
author.save
book = Book.new
book.disable_persistence
author.books << book # nothing will be saved

Fourth example:

author = Author.new
author.save
book = Book.new
author.disable_persistence
author.books << book # will be saved indeed, because the book has enabled persistency

Fifth example:

author = Author.new
Author.disable_persistence
author.save # will return false and will not save

I hope the above answers your question, or at least is helpful somehow.

Upvotes: 2

VMOrtega
VMOrtega

Reputation: 1978

You can create a Transaction and throw an Exception for Rollback any changes.

Upvotes: 1

Justin D.
Justin D.

Reputation: 4976

You could add a before_save filter to check for the value of a boolean field (say editable) that redirects to the root url if set to false. The block will end before the the create/save/update methods are called.

Upvotes: 2

Related Questions