Search code examples
ruby-on-railsmodelreadonlyreadonly-attribute

How to set read-only on a rails model using a attribute within the model?


How can I set a model to be read-only every time that it is accessed if an attribute within the same model is set to true?

I have looked everywhere and the model read only seems to have very little documentation and even web results.

Edit (Additional Info): I have two methods in my Model (application.rb) - not in private

  def lock()
    self.locked = true
    save(validate: false)
  end

  def unlock()
    self.locked = false
    save(validate: false)
  end

I call them from my applications controller on update with:

if params[:application][:locked] == false
  @application.unlock
  return
elsif params[:application][:locked] == true
  @application.lock
  return
end

and in the Model (application.rb) I have - not in private:

  def readonly?
    locked == true
  end

Solution

  • Updated:

    # app/models/application.rb
    
    # I highly suggest renaming `Application` into something else because, Rails
    # already has a same defined constant name `Application` which is defined in your
    # app/config/application.rb
    
    class Application < ApplicationRecord
      def lock!
        # depending on your use-case I'll do an `update` below instead
        # self.lock = true
        update!(locked: true)
      end
    
      def unlock!
        # self.lock = false
        update!(locked: false)
      end
    end
    
    # app/models/user.rb
    class User < ApplicationRecord
      belongs_to :application
    
      def readonly?
        # this is still subject to race-condition even though already `reloaded`
        application.reload.locked || some_user_attribute == 'HELLO WORLD!'
      end
    end
    
    # app/models/comment.rb
    class Comment < ApplicationRecord
      belongs_to :application
    
      def readonly?
        # this is still subject to race-condition even though already `reloaded`
        application.reload.locked || some_comment_attribute_like_is_disabled?
      end
    end
    

    Notice that I added a belongs_to association there because you'll most likely need this because your Application as you said is actually already a normal model anyway. If you do not have this association, and are setting the locked internally as a class instance variable of your Application class (i.e. you have @locked class instance variable), then (depending on your requirements), you'll have problems with 1) persistency because each request (per different process/server) will default to locked = nil (which might or might not be a problem to you), and also 2) concurrency because threads share the value of this class instance variable, which means that simultaneous requests would need this @locked value be evaluated independently; which becomes potentially dangerous if @locked is set to true in one thread, while on another @locked is overidden and is set to false. But if these are not a problem, I can still update my answer to not use belongs_to :application; let me know.