Prologue

Probably all the web (and maybe not only the web) developers sooner or later had to deal with such a task as
user options . What I mean the easiest way to show in the picture. By options, we mean checkboxes (that is, the option is either there or not). Usually there are not too many such options, about 2-10. Usually they are indicated when registering and / or editing a profile.
Formulation of the problem
As a rule, the number of these settings (and the settings themselves) can vary, so it would be nice to implement the mechanism of these options in such a way that adding / changing / deleting them was as simple as possible while changing as little code as possible. I'll tell you how you can solve it and how I solved this task within the framework of the Ruby on Rails framework.
Implementation options
In general, I usually ask this question at the interview, I heard a lot of answers. One of them is to use
two tables : one for options, the other for user-option communication (there is a record - it means there is a tick in the form).
Benefits:
Disadvantages:
- join tables to find out if the option is set
- nontrivial view code
Let me explain the second point: I would like to have something similar to:
<% = f.check_box: send_mail_on_friend_request%>
Of course, this can be done, but the code in the model will not be trivial.
')
Another option for options is to use one column - the text, which stores the options in serialized form
class User
serialize: options, Hash
end
In this example, it is unclear what the user’s options may be at all, let's improve:
class User
OPTION_1 =: key_1
OPTION_2 =: key_2
serialize: options, Hash
def option_1 = (value)
self.options [: key_1] = value
end
def option_2 = (value)
self.options [: key_2] = value
end
def option_1
self.options [: key_1]
end
def option_2
self.options [: key_2]
end
end
Already clearer, but too many methods: what if there are 20 options? This problem is perfectly solved by define_method (an example of its use will be lower). But still, let's think: after all, an option, it’s just a checkbox, just a bit - either cocked or not (yes, yes, as a woman’s intuition :), why serialize, deserialize? Can it be made easier?
My implementation
Let's think how many such options can there be? I think a maximum of 10, well, 20, the more the user will not fill. And what if you keep the options as usual? Not a bad idea, just have to remember working with bits:
class User <ActiveRecord :: Base
NOTIFY_ON_MAIL = 0b0001
NOTIFY_ON_WALL_POST = 0b0010
NOTIFY_ON_FRIEND_REQUEST = 0b0100
NOTIFY_ON_COMMENT_ANSWER = 0b1000
def notify_on_mail
self.options & NOTIFY_ON_MAIL! = 0
end
def notify_on_mail = (value)
if value! = "0"
self.options | = const
else
self.options & = ~ const
end
end
end
So good. Now you can generalize for any number of options:
class User <ActiveRecord :: Base
NOTIFY_ON_MAIL = 0b0001
NOTIFY_ON_WALL_POST = 0b0010
NOTIFY_ON_FRIEND_REQUEST = 0b0100
NOTIFY_ON_COMMENT_ANSWER = 0b1000
[NOTIFY_ON_MAIL, NOTIFY_ON_WALL_POST, NOTIFY_ON_FRIEND_REQUEST, NOTIFY_ON_COMMENT_ANSWER] .each do | notifer |
const = self.const_get (notifer)
define_method notifer.downcase.to_sym do
(mail_options & const)! = 0
end
define_method "# {notifer.downcase} =". to_sym do | flag |
if flag! = "0"
self.mail_options | = const
else
self.mail_options & = ~ const
end
end
end
end
Now to add a new option you need to change just two lines. Hmm, what if you reduce everything to one? Result:
class User <ActiveRecord :: Base
NOTIFY_ON_MAIL = 0b0001
NOTIFY_ON_WALL_POST = 0b0010
NOTIFY_ON_FRIEND_REQUEST = 0b0100
NOTIFY_ON_COMMENT_ANSWER = 0b1000
constants.grep (/ ^ NOTIFY _ /). each do | notifer |
const = self.const_get (notifer)
define_method notifer.downcase.to_sym do
(mail_options & const)! = 0
end
define_method "# {notifer.downcase} =". to_sym do | flag |
if flag! = "0"
self.mail_options | = const
else
self.mail_options & = ~ const
end
end
end
end
Fields for views can also be generated in a loop.
I hope a little note brought you some thoughts :) All the best to you!