|
| 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 | +* 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. |
| 11 | +* The ternary update restricts the support of total no. of flags to 15. |
| 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 | +``` |
| 120 | + |
| 121 | + |
0 commit comments