Created
October 19, 2015 21:48
-
-
Save jnettome/b07ddf4b6caab0cd8ce4 to your computer and use it in GitHub Desktop.
BitmaskOptions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module BitmaskOptions | |
| extend ActiveSupport::Concern | |
| include ActiveModel::Serialization | |
| included do | |
| mattr_accessor :all_options, :all_option_pairs, instance_writer: false | |
| end | |
| # This is a low level method that usually won't be used in public methods | |
| #http://railscasts.com/episodes/189-embedded-association | |
| def bitmask_options=(options) | |
| self.bitmask = | |
| (options.map(&:to_s) & all_options) | |
| .map { |option| 1 << all_options.index(option) } | |
| .reduce(:|) || 0 | |
| end | |
| # This is a low level method that usually won't be used in public methods | |
| def bitmask_options | |
| all_options.select do |option| | |
| ( (bitmask || 0) & 1 << all_options.index(option) ).nonzero? | |
| end | |
| end | |
| def bitmask_values | |
| result = {} | |
| current_options = bitmask_options | |
| self.class.all_options.each do |option| | |
| result[option] = current_options.include? option | |
| end | |
| result | |
| end | |
| module ClassMethods | |
| # Given all the permutations ..00, ..01, ..10, ..11 ... | |
| # select only the ones that match the given option | |
| def bitmasks_for(option) | |
| ref_bitmask = 1 << all_options.index(option) | |
| max_value = (1 << all_options.length) - 1 | |
| (0..max_value).select{ |i| i & ref_bitmask > 0 } | |
| end | |
| # This is the macro used to initialize the model with bitmask attributes | |
| # For example, in Class User we have: | |
| # | |
| # has_bitmask_options [ | |
| # ['accepts_email', 'Aceita receber SMS'], | |
| # ['accepts_sms', 'Aceita receber e-mails'], | |
| # ... | |
| # ] | |
| def has_bitmask_options(all_option_pairs) | |
| # Class parameters | |
| self.all_option_pairs = all_option_pairs | |
| self.all_options = all_option_pairs.map{ |o| o[0] } | |
| all_options.each do |option| | |
| # Helper methods | |
| define_method(option) { self.bitmask_options.include? option } | |
| define_method("#{option}?") { self.bitmask_options.include? option } | |
| define_method("#{option}=") do |active| | |
| change_mask = 1 << all_options.index(option) | |
| self.bitmask = | |
| if active.to_bool | |
| (self.bitmask || 0) | change_mask | |
| else | |
| (self.bitmask || 0) & (~change_mask) | |
| end | |
| end | |
| # Query scopes | |
| scope_name = | |
| case option | |
| when /\Aaccepts_/ then option.gsub(/\Aaccepts/, 'that_accept').to_sym | |
| when /\Ahas_/ then option.gsub(/\Ahas/, 'with').to_sym | |
| else option.to_sym | |
| end | |
| scope scope_name, ->{ where(bitmask: bitmasks_for(option)) } | |
| end | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment