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

1.6k Views Asked by At

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
1

There are 1 best solutions below

7
On BEST ANSWER

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.