How can I set some attributes of the profile model, is public (visible) or private (invisible) for another user?

I have a profile model and it has many attributes, such as email, image, age, address, etc. The end user can make some attributes private so that other users cannot view them. I solved this problem by adding a column to the private_attr table and serializing it to store the hash, for example: -

 {email: true, address: true, age: false } 

Here, attributes as a key with the value true are considered private and are not shown to the user, except for those to whom they belong.

I want to know that this is the best way to solve this problem, or is there any other way. Thanks in advance.

+5
source share
4 answers

I think you can only serialize fields that the user wants to be private in the array (instead of a hash). Like [:email, :address] (using your example)

Then, when you render the view, just check to see if this field is present in this column. Sort of

 <%= user.email unless user.private_fields.include?(:email) %> 

You can even extract this logic for a view helper to avoid duplication.

 <%= show_if_allowed(user, :email) %> 

Then create a helper helper

 def show_if_allowed(user, field) user[field] unless user.private_fields.include?(field) end 
+5
source

You asked if there are other ways. Of course. Whether it's better is up to you.

Many columns

You can create additional columns for each attribute. email_protected: true / false (etc.). If you have only 3 columns, this is not so bad, but it will not scale well if you protect many attributes.

From many to many relationships

You may have an additional model, such as ProtectedAttribute. Profile has_many ProtectedAttributes and stores data in separate tables instead of serialization. This excludes serialized data in a single column.

Further information is available upon request.

 # I'm using User instead of Profile as Profile is a protected word in Rails class User < ActiveRecord::Base has_many :profile_protected_attributes has_many :protected_attributes, through: :profile_protected_attributes def protect_attribute?(name) protected_attributes.where(name: name).present? end def show_attribute?(name) !protect_attribute(name) end def protect_attribute(name) return if self.protect_attribute?(name) protected_attributes << ProtectedAttribute.find_by_name(name) end def unprotect_attribute(name) protected_attributes.delete(ProtectedAttribute.find_by_name(name)) end end class ProtectedAttribute < ActiveRecord::Base has_many :profile_protected_attributes has_many :users, through: :profile_protected_attributes end # The join model class ProfileProtectedAttribute < ActiveRecord::Base belongs_to :user belongs_to :protected_attribute end 

Migrations (you need to configure if you adhere to the profile):

 class CreateProtectedAttributes < ActiveRecord::Migration def change create_table :protected_attributes do |t| t.string :name t.timestamps end end end class CreateProfileProtectedAttributes < ActiveRecord::Migration def change create_table :profile_protected_attributes do |t| t.integer :user_id t.integer :protected_attribute_id t.timestamps end end end 
+2
source

Actually, this is what I would do if I had to deal with 8-10 attributes. But if you have too many attributes for the Profile model class and you have complex logic for displaying these attributes based on a user group, public, common, etc., then I would advise you to move this to a separate model class, say: " ProfileConfiguration or ProfileSetting ", which will support each attribute at the row level, or you can move these settings to Redis, where the structure will look like: user_id: {attribute_name: true, type: 'type_name'} , but then there is a drawback that you will depend from the availability of the Redis server.

Now, in your case:

 serialize :profile_preferences, Hash 

and then you support it (as you mentioned, just the opposite for their purpose):

 {email: false, address: false, age: true } 

However, you can continue and create some convenient methods that you can call in your profile object:

 after_initialize :load_profile_preferences private def load_profile_preferences profile_preferences.each do |attr, value| self.class.send(:define_method, "show_#{attr.to_s}?") { value } end end 

Now will you get handy methods like show_email? show_address? and show_age? on Profile a class object that you can delegate to a User instance of the class. So now you can do something similar in your views, for example:

 <%= "Email: {user.email}" if user.show_email? %> <%= "Address: {user.address}" if user.show_address? %> <%= "Age: {user.age}" if user.show_age? %> 
+2
source

You can use a gem called cancan to process the rights that you define for different users that you define in the ruby ​​file ( ability.rb ). Then I suggest that you can have a different serializer for one resource, each serializer designed for a specific user role is easy to do with active-model-serializer .

+1
source

Source: https://habr.com/ru/post/1204176/


All Articles