module Bitfields::ClassMethods
Public Instance Methods
bitfield(column, *args)
click to toggle source
# File lib/bitfields.rb, line 42 def bitfield(column, *args) column = column.to_sym options = extract_bitfield_options args bitfield_args << [column, options.dup] store_bitfield_values column, options add_bitfield_methods column, options end
bitfield_bits(values)
click to toggle source
# File lib/bitfields.rb, line 51 def bitfield_bits(values) bits = bitfields.values.reduce({}, :merge) values.sum { |bit, on| on ? bits.fetch(bit) : 0 } end
bitfield_column(bit_name)
click to toggle source
# File lib/bitfields.rb, line 56 def bitfield_column(bit_name) found = bitfields.detect{|_, bits| bits.keys.include?(bit_name.to_sym) } raise "Unknown bitfield #{bit_name}" unless found found.first end
bitfield_sql(bit_values, options={})
click to toggle source
# File lib/bitfields.rb, line 62 def bitfield_sql(bit_values, options={}) bits = group_bits_by_column(bit_values).sort_by{|c,_| c.to_s } bits.map{|column, bit_values| bitfield_sql_by_column(column, bit_values, options) } * ' AND ' end
set_bitfield_sql(bit_values)
click to toggle source
# File lib/bitfields.rb, line 67 def set_bitfield_sql(bit_values) bits = group_bits_by_column(bit_values).sort_by{|c,_| c.to_s } bits.map{|column, bit_values| set_bitfield_sql_by_column(column, bit_values) } * ', ' end
Private Instance Methods
add_bitfield_methods(column, options)
click to toggle source
# File lib/bitfields.rb, line 87 def add_bitfield_methods(column, options) bitfields[column].keys.each do |bit_name| if options[:added_instance_methods] != false define_method(bit_name) { bitfield_value(bit_name) } define_method("#{bit_name}?") { bitfield_value(bit_name) } define_method("#{bit_name}=") { |value| set_bitfield_value(bit_name, value) } # Dirty methods usable in before_save contexts define_method("#{bit_name}_was") { bitfield_value_was(bit_name) } alias_method "#{bit_name}_in_database", "#{bit_name}_was" define_method("#{bit_name}_change") { bitfield_value_change(bit_name) } alias_method "#{bit_name}_change_to_be_saved", "#{bit_name}_change" define_method("#{bit_name}_changed?") { bitfield_value_change(bit_name).present? } alias_method "will_save_change_to_#{bit_name}?", "#{bit_name}_changed?" define_method("#{bit_name}_became_true?") do value = bitfield_value(bit_name) value && send("#{bit_name}_was") != value end define_method("#{bit_name}_became_false?") do value = bitfield_value(bit_name) !value && send("#{bit_name}_was") != value end # Dirty methods usable in after_save contexts define_method("#{bit_name}_before_last_save") { bitfield_value_before_last_save(bit_name) } define_method("saved_change_to_#{bit_name}") { saved_change_to_bitfield_value(bit_name) } define_method("saved_change_to_#{bit_name}?") { saved_change_to_bitfield_value(bit_name).present? } end if options[:scopes] != false scope bit_name, bitfield_scope_options(bit_name => true) scope "not_#{bit_name}", bitfield_scope_options(bit_name => false) end end include Bitfields::InstanceMethods end
bit_values_to_on_off(column, bit_values)
click to toggle source
# File lib/bitfields.rb, line 172 def bit_values_to_on_off(column, bit_values) on = off = 0 bit_values.each do |bit_name, value| bit = bitfields[column][bit_name] value ? on += bit : off += bit end [on, off] end
bitfield_scope_options(bit_values)
click to toggle source
# File lib/bitfields.rb, line 128 def bitfield_scope_options(bit_values) -> { where(bitfield_sql(bit_values)) } end
bitfield_sql_by_column(column, bit_values, options={})
click to toggle source
# File lib/bitfields.rb, line 132 def bitfield_sql_by_column(column, bit_values, options={}) mode = options[:query_mode] || (bitfield_options[column][:query_mode] || :bit_operator) case mode when :in_list then max = (bitfields[column].values.max * 2) - 1 bits = (0..max).to_a # all possible bits bit_values.each do |bit_name, value| bit = bitfields[column][bit_name] # reject values with: bit off for true, bit on for false bits.reject!{|i| i & bit == (value ? 0 : bit) } end "#{table_name}.#{column} IN (#{bits * ','})" when :bit_operator on, off = bit_values_to_on_off(column, bit_values) "(#{table_name}.#{column} & #{on+off}) = #{on}" when :bit_operator_or on, off = bit_values_to_on_off(column, bit_values) result = [] result << "(#{table_name}.#{column} & #{on}) <> 0" if on != 0 result << "(#{table_name}.#{column} & #{off}) <> #{off}" if off != 0 result.join(' OR ') else raise("bitfields: unknown query mode #{mode.inspect}") end end
extract_bitfield_options(args)
click to toggle source
# File lib/bitfields.rb, line 74 def extract_bitfield_options(args) options = (args.last.is_a?(Hash) ? args.pop.dup : {}) args.each_with_index{|field,i| options[2**i] = field } # add fields given in normal args to options options end
group_bits_by_column(bit_values)
click to toggle source
# File lib/bitfields.rb, line 162 def group_bits_by_column(bit_values) columns = {} bit_values.each do |bit_name, value| column = bitfield_column(bit_name.to_sym) columns[column] ||= {} columns[column][bit_name.to_sym] = value end columns end
set_bitfield_sql_by_column(column, bit_values)
click to toggle source
# File lib/bitfields.rb, line 157 def set_bitfield_sql_by_column(column, bit_values) on, off = bit_values_to_on_off(column, bit_values) "#{column} = (#{column} | #{on+off}) - #{off}" end
store_bitfield_values(column, options)
click to toggle source
# File lib/bitfields.rb, line 80 def store_bitfield_values(column, options) self.bitfields ||= {} self.bitfield_options ||= {} bitfields[column] = Bitfields.extract_bits(options) bitfield_options[column] = options end