has_blob_bit_field 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +22 -21
- data/lib/has_blob_bit_field/accessor.rb +74 -19
- data/lib/has_blob_bit_field/extension.rb +3 -3
- data/lib/has_blob_bit_field/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84cb16ec03d0b3289a08923dbe26b84f80539290
|
4
|
+
data.tar.gz: bb148f70fc3bf032ef79f3b531abf22c32092ab0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7eb4a11088a4a8f9332a169c3c2fafabdd83b7911fbd903edafb1248daaa5b984f93eafa562e2a47ca3b3700bc22d47f88cc6e12f9c72dfb2f5ce50691da9b14
|
7
|
+
data.tar.gz: 1906c4b3239b81ead3899813c2eb707d5347342a2c8ca407399ce0635517230f8e568715ecf1d325ddec1fa1d0d9681f48883f5551d267c6db1f803caedce93e
|
data/README.md
CHANGED
@@ -2,22 +2,6 @@
|
|
2
2
|
|
3
3
|
A Rails extension to treat a binary column (blob) as a sequence of `true`/`false` flags (bits)
|
4
4
|
|
5
|
-
## Installation
|
6
|
-
|
7
|
-
Add this line to your application's Gemfile:
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
gem 'has_blob_bit_field'
|
11
|
-
```
|
12
|
-
|
13
|
-
And then execute:
|
14
|
-
|
15
|
-
$ bundle
|
16
|
-
|
17
|
-
Or install it yourself as:
|
18
|
-
|
19
|
-
$ gem install has_blob_bit_field
|
20
|
-
|
21
5
|
## Example Usage
|
22
6
|
|
23
7
|
```
|
@@ -27,33 +11,50 @@ def change
|
|
27
11
|
end
|
28
12
|
|
29
13
|
# Model:
|
30
|
-
|
31
14
|
class YourModel < ActiveRecord::Base
|
32
15
|
has_blob_field :many_flags
|
33
16
|
end
|
34
17
|
|
35
18
|
# Usage:
|
36
|
-
|
37
19
|
o = YourModel.new
|
38
20
|
o.many_flags.size # => 0
|
39
21
|
o.many_flags.size = 666 # Can be as big as 8 times the binary limit of your column
|
40
22
|
o.many_flags[42] # => false
|
41
23
|
o.many_flags[42] = true
|
42
24
|
o.many_flags[700] # => IndexError
|
25
|
+
o.many_flags.count{|f| f} # => 1
|
26
|
+
o.many_flags = [false, true, true, false, false, false, false, true]
|
27
|
+
o.many_flags.map! {|f| !f}
|
28
|
+
o.many_flags == [true, false, false, true, true, true, true, false] # => true
|
29
|
+
o.many_flags_blob_changed? # => true
|
43
30
|
```
|
44
31
|
|
45
32
|
## Notes:
|
46
33
|
|
47
34
|
* The convention is that your column has the same name as your accessing method with a `'_blob'` suffix, but you can specify the column to use when calling `has_blob_field`.
|
48
35
|
|
49
|
-
* The first flag is stored using the highest bit of the first byte.
|
36
|
+
* The blob is viewed as an array of bits where the first flag is stored using the highest bit of the first byte.
|
50
37
|
|
51
38
|
* The size is always rounded up to a multiple of 8.
|
52
39
|
|
53
|
-
* Accessing out of bounds indices raises an `IndexError`, but code could be adapted easily to return `nil` instead.
|
54
|
-
|
55
40
|
* Targets: Ruby 2.0+, Rails 3.0+
|
56
41
|
|
42
|
+
## Installation
|
43
|
+
|
44
|
+
Add this line to your application's Gemfile:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
gem 'has_blob_bit_field'
|
48
|
+
```
|
49
|
+
|
50
|
+
And then execute:
|
51
|
+
|
52
|
+
$ bundle
|
53
|
+
|
54
|
+
Or install it yourself as:
|
55
|
+
|
56
|
+
$ gem install has_blob_bit_field
|
57
|
+
|
57
58
|
## Development
|
58
59
|
|
59
60
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module HasBlobBitField
|
2
2
|
class Accessor
|
3
|
+
include Enumerable
|
4
|
+
include Comparable
|
3
5
|
attr_reader :record, :column
|
4
6
|
|
5
7
|
def initialize record, column
|
@@ -7,18 +9,21 @@ module HasBlobBitField
|
|
7
9
|
@column = column
|
8
10
|
end
|
9
11
|
|
10
|
-
def [](
|
11
|
-
|
12
|
-
val
|
12
|
+
def [](bit_index)
|
13
|
+
bit_index = check_index(bit_index)
|
14
|
+
val = byte(bit_index)
|
15
|
+
val & flag(bit_index) != 0
|
13
16
|
end
|
14
17
|
|
15
|
-
def []=(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def []=(bit_index, set)
|
19
|
+
bit_index = check_index(bit_index)
|
20
|
+
set = coerce_to_bool(set)
|
21
|
+
val = byte(bit_index)
|
22
|
+
mask = flag(bit_index)
|
23
|
+
was_set = val & mask != 0
|
24
|
+
if was_set != set
|
20
25
|
notify_of_mutation
|
21
|
-
raw_value.setbyte(
|
26
|
+
raw_value.setbyte(bit_index >> 3, val ^ mask)
|
22
27
|
end
|
23
28
|
set
|
24
29
|
end
|
@@ -37,7 +42,7 @@ module HasBlobBitField
|
|
37
42
|
if s.size > size_in_bytes
|
38
43
|
s[size_in_bytes..s.size] = ''
|
39
44
|
else
|
40
|
-
s <<
|
45
|
+
s << "\0".b * (size_in_bytes - s.size)
|
41
46
|
end
|
42
47
|
end
|
43
48
|
size_in_bits
|
@@ -48,28 +53,78 @@ module HasBlobBitField
|
|
48
53
|
|
49
54
|
def raw_value(init_nil: false)
|
50
55
|
@record.public_send(@column) ||
|
51
|
-
(init_nil &&
|
56
|
+
(init_nil && replace_raw_value(''.b)) ||
|
52
57
|
''.b
|
53
58
|
end
|
54
59
|
|
60
|
+
def replace(values)
|
61
|
+
new_raw_value = if values.class == self.class
|
62
|
+
values.raw_value
|
63
|
+
else
|
64
|
+
masks = (0..7).map {|i| flag(i) }
|
65
|
+
self.size = values.size
|
66
|
+
values.each_slice(8).map do |flags|
|
67
|
+
flags.each_with_index.inject(0) do |sum, (flag, bit_number)|
|
68
|
+
sum | (flag ? masks[bit_number] : 0)
|
69
|
+
end
|
70
|
+
end.pack('C*')
|
71
|
+
end
|
72
|
+
replace_raw_value new_raw_value
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def each
|
77
|
+
return to_enum unless block_given?
|
78
|
+
masks = (0..7).map {|i| flag(i) }
|
79
|
+
raw_value.each_byte do |byte|
|
80
|
+
masks.each do |mask|
|
81
|
+
yield mask & byte != 0
|
82
|
+
end
|
83
|
+
end
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def map!
|
88
|
+
return to_enum unless block_given?
|
89
|
+
replace(map {|b| yield b })
|
90
|
+
end
|
91
|
+
|
92
|
+
def <=>(other)
|
93
|
+
value_method = other.class == self.class ? :raw_value : :to_a
|
94
|
+
public_send(value_method) <=> other.public_send(value_method)
|
95
|
+
end
|
55
96
|
|
56
|
-
|
97
|
+
protected
|
57
98
|
def notify_of_mutation
|
58
99
|
@record.public_send :"#{@column}_will_change!"
|
59
100
|
self
|
60
101
|
end
|
61
102
|
|
62
|
-
def
|
63
|
-
|
103
|
+
def replace_raw_value(raw)
|
104
|
+
notify_of_mutation
|
105
|
+
@record.public_send(:"#{@column}=", raw)
|
106
|
+
end
|
107
|
+
|
108
|
+
def flag(bit_index)
|
109
|
+
0b1000_0000 >> (bit_index & 0b111)
|
110
|
+
end
|
111
|
+
|
112
|
+
def byte(bit_index)
|
113
|
+
raw_value.getbyte(bit_index >> 3) || out_of_bound(bit_index)
|
114
|
+
end
|
115
|
+
|
116
|
+
def coerce_to_bool(value)
|
117
|
+
raise TypeError unless value == true || value == false
|
118
|
+
value
|
64
119
|
end
|
65
120
|
|
66
|
-
def
|
67
|
-
out_of_bound(
|
68
|
-
|
121
|
+
def check_index(bit_index)
|
122
|
+
out_of_bound(bit_index) if bit_index < 0
|
123
|
+
bit_index
|
69
124
|
end
|
70
125
|
|
71
|
-
def out_of_bound(
|
72
|
-
raise IndexError, "#{
|
126
|
+
def out_of_bound(bit_index)
|
127
|
+
raise IndexError, "#{bit_index} is our of range for #{@column} which has size #{size}. Record: #{@record.inspect}}"
|
73
128
|
end
|
74
129
|
end
|
75
130
|
end
|
@@ -8,11 +8,11 @@ module HasBlobBitField
|
|
8
8
|
def has_blob_bit_field field, column: :"#{field}_blob"
|
9
9
|
class_eval <<-EVAL, __FILE__, __LINE__
|
10
10
|
def #{field}
|
11
|
-
Accessor.new self, :#{column}
|
11
|
+
@_#{field}_accessor ||= Accessor.new self, :#{column}
|
12
12
|
end
|
13
13
|
|
14
|
-
def #{field}=
|
15
|
-
|
14
|
+
def #{field}=(values)
|
15
|
+
#{field}.replace(values)
|
16
16
|
end
|
17
17
|
EVAL
|
18
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_blob_bit_field
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-Andre Lafortune
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -138,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
138
|
version: '0'
|
139
139
|
requirements: []
|
140
140
|
rubyforge_project:
|
141
|
-
rubygems_version: 2.
|
141
|
+
rubygems_version: 2.5.1
|
142
142
|
signing_key:
|
143
143
|
specification_version: 4
|
144
144
|
summary: Acces a binary column as a sequence of true/false flags
|