|
| 1 | +# FlagShihTzu_Ternary_flag_update |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +The updated functionality of the gem allow flag(s) to be set as nil, i.e neither true nor false. |
| 6 | + |
| 7 | + |
| 8 | +### Defaults (Important) |
| 9 | + |
| 10 | +* As, the rails default limit(size) of an integer is 4, the ternary update restricts the support of total no. of flags to 15. Change the limit value in the migration to increase total no. of flags. |
| 11 | +* Due to the default of `0`, *all flags* are initially set to "false". To set all flags to nil initially, assign default value to 2^(32-1)-1 i.e 2147483647 in the migration. |
| 12 | + |
| 13 | + |
| 14 | +### Bit Fields: How it stores the three values |
| 15 | + |
| 16 | +As said, FlagShihTzu uses a single integer column to store the values for all |
| 17 | +the defined flags as a [bit field][bitfield]. |
| 18 | + |
| 19 | +So, to accomodate 3 values(true, false and nil), a combination of 2 bits is used to store one of the three value via following mapping: |
| 20 | + |
| 21 | + `---+---+---+---+ |
| 22 | + | Value | Bits | |
| 23 | + | | | |
| 24 | + `---+---+---+---+ |
| 25 | + | false | 00 | |
| 26 | + `---+---+---+---+ |
| 27 | + | true | 10 | |
| 28 | + `---+---+---+---+ |
| 29 | + | nil | 11 | |
| 30 | + `---+---+---+---+ |
| 31 | + |
| 32 | +The bit position of a flag corresponds to the given key. |
| 33 | + |
| 34 | +This way, we can use [bitwise operators][bit_operation] on the stored integer value to set, unset |
| 35 | +and check individual flags. |
| 36 | + |
| 37 | + `---+---+---+---+---+---+ +---+---+---+---+---+---` |
| 38 | + | | | | | | | | |
| 39 | + Flag position | 3 | 2 | 1 | | 3 | 2 | 1 | |
| 40 | + (flag key) | | | | | | | | |
| 41 | + `---+---+---+---+---+---+ +---+---+---+---+---+---` |
| 42 | + | e | s | w | | e | s | w | |
| 43 | + | l | h | a | | l | h | a | |
| 44 | + | e | i | r | | e | i | r | |
| 45 | + | c | e | p | | c | e | p | |
| 46 | + | t | l | d | | t | l | d | |
| 47 | + | r | d | r | | r | d | r | |
| 48 | + | o | s | i | | o | s | i | |
| 49 | + | l | | v | | l | | v | |
| 50 | + | y | | e | | y | | e | |
| 51 | + | t | | | | t | | | |
| 52 | + | e | | | | e | | | |
| 53 | + | s | | | | s | | | |
| 54 | + `---+---+---+---+---+---+ +---+---+---+---+---+---` |
| 55 | + | true | false | nil | | nil | true | false | |
| 56 | + Flag Value `---+---|---+---|---+---+ +---+---|---+---|---+---` |
| 57 | + | 0 1 | 0 0 | 1 1 | | 1 1 | 0 1 | 0 0 | |
| 58 | + `---+---+---+---|---+---+ +---+---|---+---|---+---` |
| 59 | + Bit Value | 32 16 | 8 4 | 2 1 | = 16 + 2 + 1 = 19 | 32 16 | 8 4 | 2 1 | = 32 + 16 + 4 = 52 |
| 60 | + `---+---+---+---|---+---+ +---+---|---+---|---+---` |
| 61 | + |
| 62 | +Read more about [bit fields][bit_field] here: http://en.wikipedia.org/wiki/Bit_field |
| 63 | + |
| 64 | + |
| 65 | +### Bang Method for nil value |
| 66 | + |
| 67 | +When setting the `:bang_methods` option to true, the following method also gets defined: |
| 68 | + |
| 69 | + Spaceship#electrolytes_nil! # will save the bitwise equivalent of electrolytes = nil on the record |
| 70 | + |
| 71 | +which clears the current value of the electrolytes flag. |
| 72 | + |
| 73 | + |
| 74 | +### Generated named scope for nil flags |
| 75 | + |
| 76 | +The following new named scope(s) become available: |
| 77 | + |
| 78 | +```ruby |
| 79 | +Spaceship.warpdrive_nil # :conditions => "(spaceships.flags in (3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95))" |
| 80 | +``` |
| 81 | + |
| 82 | + |
| 83 | +### Support for manually building conditions |
| 84 | + |
| 85 | +The following class method gets added for nil condition for supporting manually building |
| 86 | +ActiveRecord conditions: |
| 87 | + |
| 88 | +```ruby |
| 89 | +Spaceship.warpdrive_nil_condition # :conditions => "(spaceships.flags in (3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95))" |
| 90 | +``` |
| 91 | + |
| 92 | + |
| 93 | +### Query mode |
| 94 | + |
| 95 | +While the default way of building the SQL conditions uses an `IN()` list |
| 96 | +(as shown above) and the same is also used in flag_shih_tzu, this approach will not work well in this gem for higher number of flags, |
| 97 | +as the value list for `IN()` grows a lot faster. |
| 98 | + |
| 99 | +So, in this gem, flag query mode is changed to `:bit_operator` |
| 100 | +from `:in_list` |
| 101 | + |
| 102 | +This will modify the generated condition and named_scope methods to use bit |
| 103 | +operators in the SQL instead of an `IN()` list: |
| 104 | + |
| 105 | +```ruby |
| 106 | +Spaceship.warpdrive_condition # "(spaceships.flags & 3 = 1)", |
| 107 | +Spaceship.not_warpdrive_condition # "(spaceships.flags & 3 = 0)", |
| 108 | +Spaceship.warpdrive_nil_condition # "(spaceships.flags & 3 = 3)", |
| 109 | +Spaceship.shields_condition # "(spaceships.flags & 12 = 4)", |
| 110 | +Spaceship.not_shields_condition # "(spaceships.flags & 12 = 0)", |
| 111 | +Spaceship.shields_nil_condition # "(spaceships.flags & 12 = 12)", |
| 112 | + |
| 113 | +Spaceship.warpdrive # :conditions => "(spaceships.flags & 3 = 1)" |
| 114 | +Spaceship.not_warpdrive # :conditions => "(spaceships.flags & 3 = 0)" |
| 115 | +Spaceship.warpdrive_nil # :conditions => "(spaceships.flags & 3 = 3)" |
| 116 | +Spaceship.shields # :conditions => "(spaceships.flags & 12 = 4)" |
| 117 | +Spaceship.not_shields # :conditions => "(spaceships.flags & 12 = 0)" |
| 118 | +Spaceship.shields_nil # :conditions => "(spaceships.flags & 12 = 12)" |
| 119 | +``` |
0 commit comments