has_blob_bit_field 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 49981851b7b6ee4e6f47c6dd40d972da94f2214c
4
- data.tar.gz: 42fd5835bb8f988c99e3ec6e79c9d08e9112bccc
3
+ metadata.gz: 84cb16ec03d0b3289a08923dbe26b84f80539290
4
+ data.tar.gz: bb148f70fc3bf032ef79f3b531abf22c32092ab0
5
5
  SHA512:
6
- metadata.gz: 55d8342bd3714c23dcd756a1f075f5eb77ae464e78f4d674b3d02181e1c1cd9712c676a6eb680570cb33967c6647e2bb32518de8a672dae5e24dd0ef4bf90b3d
7
- data.tar.gz: dcb70d44e7418e9f96f289c80a13437fc0c96e75ef4437b6355294b0ec2c698ce617d448502504bb9a87a14c0ac3ecf03e650d97a2746955cb80c82215867c99
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 [](index)
11
- val = byte(index)
12
- val & flag(index) != 0
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 []=(index, set)
16
- set = !!set # force to true/false
17
- val = byte(index)
18
- mask = flag(index)
19
- if (val & mask != 0) != set
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(index >> 3, val ^ mask)
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 << Array.new(size_in_bytes - s.size, 0).pack('C*')
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 && notify_of_mutation && @record.public_send(:"#{@column}=", ''.b)) ||
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
- private
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 flag(index)
63
- 0b1000_0000 >> (index & 0b111)
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 byte(index)
67
- out_of_bound(index) unless index >= 0
68
- raw_value.getbyte(index >> 3) || out_of_bound(index)
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(index)
72
- raise IndexError, "#{index} is our of range for #{@column} which has size #{size}. Record: #{@record.inspect}}"
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
- raise "Don't set the pseudo field #{field} directly, use []= on it"
14
+ def #{field}=(values)
15
+ #{field}.replace(values)
16
16
  end
17
17
  EVAL
18
18
  end
@@ -1,3 +1,3 @@
1
1
  module HasBlobBitField
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  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.1
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-20 00:00:00.000000000 Z
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.2.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