banditmask 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 89f4119902ba1674931d97b169c39ce1037ae2ae
4
- data.tar.gz: a5db82f06d10b4bdc8ff6927d9cc872df3f89721
3
+ metadata.gz: c07c68905cfe2a56b3ba19fa9192604e505fe06e
4
+ data.tar.gz: 5787ac69e4472405d2e0408203e788128eaabb19
5
5
  SHA512:
6
- metadata.gz: ec236a1784b37496fde1b63972587ff6bd57249dd7e68b3ae57107b52092274172fc99b188b31e2a0b7b97f04cf4da1f955356c339fb4c47fdb0bb3c0a5a91ce
7
- data.tar.gz: 280fb89f74711b141be7a505e4f05756443276322407df26a326681a705116583089af90b4b66aa98349f35238fb9e101b0f775542fd4ece051ceb4d1a1629a2
6
+ metadata.gz: 5535575ab548f020d6043df8927ba7da1fe90bf094c76894890894f1c9c153dabf6fab2501ed23be227c169a8d53af94c41b579a6631777b0c30611876a51c9e
7
+ data.tar.gz: b0a80aa7a44e9e02d32465d9bc4c200d697bb5fdefe63239328faad9255e3b387d06fbf5fe44717017ae239be4ec500915b23fc9a70ac3bc3cb8b5eb10e3b3a9
data/README.md CHANGED
@@ -22,7 +22,9 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- Create a class which inherits from `BanditMask`, and declare the available bit
25
+ ### BanditMask
26
+
27
+ Create a class which inherits from BanditMask, and declare the available bit
26
28
  names and their corresponding values.
27
29
 
28
30
  ```ruby
@@ -47,25 +49,29 @@ mask = ChmodMask.new current_bitmask
47
49
  Enable bits by name.
48
50
 
49
51
  ```ruby
50
- mask << :read << :write
52
+ mask << :read << :execute
51
53
  ```
52
54
 
53
55
  Ask whether specific bits are enabled.
54
56
 
55
57
  ```ruby
56
- mask.include? :read # => true
57
- mask.include? :write # => true
58
- mask.include? :execute # => false
58
+ mask.include? :read # => true
59
+ mask.include? :write # => false
60
+ mask.include? :execute # => true
61
+ mask.include? :read, :write # => false
62
+ mask.include? :read, :execute # => true
59
63
  ```
60
64
 
61
65
  Retrieve a list of all currently enabled bits.
62
66
 
63
67
  ```ruby
64
- mask.bits # => [:read, :write]
68
+ mask.bits # => [:read, :execute]
65
69
  ```
66
70
 
67
- In an class with a bitmask attribute, extend `BanditMask::Banditry` and call
68
- `BanditMask::Banditry.bandit_mask` to add accessor methods for working with the
71
+ ### BanditMask::Banditry
72
+
73
+ In a class with a bitmask attribute, extend BanditMask::Banditry and call
74
+ BanditMask::Banditry.bandit_mask to add accessor methods for working with the
69
75
  bitmask attribute.
70
76
 
71
77
  ```ruby
@@ -77,28 +83,35 @@ class ObjectWithBitmaskAttribute
77
83
  end
78
84
 
79
85
  obj = ObjectWithBitmaskAttribute.new
80
- obj.bitmask = 0b011
86
+ obj.bitmask = 0b001
81
87
  ```
82
88
 
83
- This gives you a reader method which delegates to `BanditMask#bits`.
89
+ This gives you a reader method which returns the BanditMask representation
90
+ of the bitmask attribute.
84
91
 
85
92
  ```ruby
86
- obj.bits # => [:read, :write]
93
+ obj.bits # => #<ChmodMask:0x007f941b9518c8 @bitmask=1>
87
94
  ```
88
95
 
89
- It also gives you a writer method which lets you overwrite the bitmask.
96
+ It also gives you a writer method which lets you modify the bitmask. The
97
+ writer accepts BanditMask objects or an Array of bits.
90
98
 
91
99
  ```ruby
100
+ obj.bits |= :write
101
+ obj.bitmask # => 3
92
102
  obj.bits = [:read, :execute]
93
103
  obj.bitmask # => 5
94
104
  ```
95
105
 
96
- Finally, it gives you a query method for checking whether a particular bit is
106
+ Finally, it gives you a query method for checking whether particular bits are
97
107
  set on the bitmask.
98
108
 
99
109
  ```ruby
100
- obj.has? :read # => true
101
- obj.has? :write # => false
110
+ obj.bits? :read # => true
111
+ obj.bits? :write # => false
112
+ obj.bits? :execute # => true
113
+ obj.bits? :read, :write # => false
114
+ obj.bits? :read, :execute # => true
102
115
  ```
103
116
 
104
117
  ## Development
data/lib/banditmask.rb CHANGED
@@ -4,8 +4,8 @@ require 'banditmask/banditry'
4
4
  class BanditMask
5
5
  attr_reader :bitmask
6
6
 
7
- def initialize(bitmask = 0b0) # :nodoc:
8
- @bitmask = bitmask
7
+ def initialize(bitmask = nil) # :nodoc:
8
+ @bitmask = bitmask || 0b0
9
9
  end
10
10
 
11
11
  ##
@@ -42,12 +42,12 @@ class BanditMask
42
42
  ##
43
43
  # Returns an array of names of the currently enabled bits.
44
44
  #
45
- # class BanditMask
45
+ # class MyMask < BanditMask
46
46
  # bit :read, 0b01
47
47
  # bit :write, 0b10
48
48
  # end
49
49
  #
50
- # mask = BanditMask.new 0b01
50
+ # mask = MyMask.new 0b01
51
51
  # mask.bits # => [:read]
52
52
  def bits
53
53
  self.class.bits.select { |bit, _| include? bit }.keys
@@ -57,32 +57,83 @@ class BanditMask
57
57
  ##
58
58
  # Enables the bit named +bit+. Returns +self+, so calls to #<< can be
59
59
  # chained. (Think Array#<<.) Raises +ArgumentError+ if +bit+ does not
60
- # correspond to a bit that was previously declared with BanditMask.bit.
60
+ # correspond to a bit that was previously defined with BanditMask.bit.
61
61
  #
62
- # class BanditMask
62
+ # class MyMask < BanditMask
63
63
  # bit :read, 0b01
64
64
  # bit :write, 0b10
65
65
  # end
66
66
  #
67
- # mask = BanditMask.new
68
- # mask << :read << :write
67
+ # mask = MyMask.new # => #<MyMask:0x007f9ebd44ae40 @bitmask=0>
68
+ # mask << :read << :write # => #<MyMask:0x007f9ebd44ae40 @bitmask=3>
69
69
  def <<(bit)
70
70
  @bitmask |= bit_value(bit)
71
71
  self
72
72
  end
73
73
 
74
74
  ##
75
- # Returns false if any bit in +bits+ is not enabled. Returns true otherwise.
76
- # Raises +ArgumentError+ if +bits+ is empty or if any element in +bits+ does
77
- # not correspond to bit that was previously declared with BanditMask.bit.
75
+ # Returns a new instance with the current bitmask plus +bit+. Raises
76
+ # +ArgumentError+ if +bit+ does not correspond to a bit that was previously
77
+ # defined by BanditMask.bit.
78
+ #
79
+ # class MyMask < BanditMask
80
+ # bit :read, 0b01
81
+ # bit :write, 0b10
82
+ # end
78
83
  #
79
- # class BanditMask
84
+ # mask = MyMask.new 0b01
85
+ # mask | :write # => #<MyMask:0x007f9e0bcf5d90 @bitmask=3>
86
+ def |(bit)
87
+ self.class.new bitmask | bit_value(bit)
88
+ end
89
+
90
+ ##
91
+ # Returns +true+ if +other+ is an instance of the same class as +self+ and
92
+ # they have the same bitmask.
93
+ #
94
+ # class MyMask < BanditMask
95
+ # bit :read, 0b01
96
+ # bit :write, 0b10
97
+ # end
98
+ #
99
+ # class MyOtherMask < BanditMask
100
+ # bit :chocolate, 0b01
101
+ # bit :vanilla, 0b10
102
+ # end
103
+ #
104
+ # a = MyMask.new 0b1
105
+ # b = MyMask.new 0b1
106
+ # c = MyMask.new 0b0
107
+ # d = MyOtherMask.new 0b1
108
+ #
109
+ # a == b # => true
110
+ # a == c # => false
111
+ # a == d # => false
112
+ def ==(other)
113
+ other.class == self.class && other.bitmask == bitmask
114
+ end
115
+
116
+ alias_method :eql?, :==
117
+
118
+ ##
119
+ # Returns an object hash. Two BanditMask objects have identical hashes if
120
+ # they have identical bitmasks and are instances of the same class.
121
+ def hash
122
+ [bitmask, self.class].hash
123
+ end
124
+
125
+ ##
126
+ # Returns true if every bit in +bits+ is enabled and false otherwise. Raises
127
+ # +ArgumentError+ if +bits+ is empty or if any element in +bits+ does not
128
+ # correspond to a bit that was previously defined with BanditMask.bit.
129
+ #
130
+ # class MyMask < BanditMask
80
131
  # bit :read, 0b001
81
132
  # bit :write, 0b010
82
133
  # bit :execute, 0b100
83
134
  # end
84
135
  #
85
- # mask = BanditMask.new 0b101
136
+ # mask = MyMask.new 0b101
86
137
  #
87
138
  # mask.include? :read # => true
88
139
  # mask.include? :write # => false
@@ -97,6 +148,9 @@ class BanditMask
97
148
 
98
149
  private
99
150
 
151
+ ##
152
+ # Returns the integer value for the bit named +bit+. Raises +ArgumentError+
153
+ # if +bit+ has not been previously defined with BanditMask.bit.
100
154
  def bit_value(bit)
101
155
  self.class.bits.fetch(bit) do
102
156
  raise ArgumentError, "undefined bit: #{bit.inspect}"
@@ -1,54 +1,88 @@
1
1
  class BanditMask
2
+ class MethodCollisionError < StandardError
3
+ end
4
+
2
5
  module Banditry
3
6
  ##
4
- # Creates wrapper methods for reading and writing the bitmask stored in
5
- # +attribute+ using the class +with+. +with+ defaults to BanditMask, but
6
- # you can (and probably) should define your own class descending from
7
- # +BanditMask+ to fill this role. The name of the accessor methods will be
8
- # derived from +as+, e.g., if +as+ is +:foo+, the reader method will be
9
- # +:foo+ and the writer will be +:foo=+.
7
+ # Creates wrapper methods for reading, writing, and querying the bitmask
8
+ # stored in +attribute+ using the class +with+. +with+ defaults to
9
+ # BanditMask, but you can (and probably should) define your own class
10
+ # inheriting from +BanditMask+ to fill this role. The name of the accessor
11
+ # methods will be derived from +as+, e.g., if +as+ is +:foo+, the methods
12
+ # will be named +:foo+, +:foo=+, and +:foo?+.
10
13
  #
11
- # The reader method will call BanditMask#bits to get an array of the
12
- # enabled bit names represented by +attribute+.
14
+ # The reader method will return a BanditMask representation of +attribute+.
13
15
  #
14
- # The writer method will replace the current bitmask with an Integer
15
- # representation of a new BanditMask built up using BanditMask#<<.
16
+ # The writer method overwrites the current bitmask if with an Array of
17
+ # bits, or it updates the current bitmask if sent an individual bit, e.g.,
18
+ # using +#|+.
16
19
  #
17
- # In addition to the accessor methods, a method named +has?+ will be added
18
- # which delegates to the BanditMask#include?.
20
+ # In addition to the accessor methods, a query method that delegates to
21
+ # BanditMask#include? will be added.
19
22
  #
20
- # class ThingMask < BanditMask
21
- # # ...
22
- # # bit :foo, 0b1
23
- # # ...
23
+ # class FileMask < BanditMask
24
+ # bit :r, 0b001
25
+ # bit :w, 0b010
26
+ # bit :e, 0b100
24
27
  # end
25
28
  #
26
- # class Thing
27
- # attr_accessor :bitmask
29
+ # class FileObject
30
+ # attr_accessor :mode_mask
28
31
  #
29
32
  # extend BanditMask::Banditry
30
- # bandit_mask :bitmask, as: :bits, with: ThingMask
33
+ # bandit_mask :mode_mask, as: :mode, with: FileMask
31
34
  # end
35
+ #
36
+ # file = FileObject.new
37
+ # file.mode_mask # => nil
38
+ # file.mode |= :r
39
+ # file.mode_mask # => 1
40
+ # file.mode |= :w
41
+ # file.mode_mask # => 3
42
+ # file.mode = [:r, :x]
43
+ # file.mode_mask # => 5
32
44
  def bandit_mask(attribute, as:, with: BanditMask)
33
- cls = with
34
- wrapper = as
35
-
36
45
  class_eval do
37
- ##
38
- # A reader method which instances a new BanditMask object and calls
39
- # BanditMask#bits.
40
- define_method wrapper do
41
- cls.new(send(attribute)).bits
42
- end
43
-
44
- define_method :"#{wrapper}=" do |bits|
45
- mask = bits.reduce(cls.new) { |mask, bit| mask << bit }
46
- send :"#{attribute}=", Integer(mask)
47
- end
48
-
49
- define_method :has? do |*bits|
50
- cls.new(send(attribute)).include? *bits
51
- end
46
+ generate_reader attribute, as, with
47
+ generate_writer attribute, as, with
48
+ generate_query attribute, as, with
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def generate_reader(attr, virt, cls)
55
+ respond_to? virt and
56
+ raise MethodCollisionError, "method `#{virt}` already exists"
57
+
58
+ define_method virt do
59
+ instance_variable_get(:"@#{virt}") ||
60
+ instance_variable_set(:"@#{virt}", cls.new(send(attr)))
61
+ end
62
+ end
63
+
64
+ def generate_writer(attr, virt, cls)
65
+ respond_to? :"#{virt}=" and
66
+ raise MethodCollisionError, "method `#{virt}=` already exists"
67
+
68
+ define_method :"#{virt}=" do |bits|
69
+ mask = case bits
70
+ when BanditMask
71
+ bits
72
+ else
73
+ bits.inject(cls.new) { |bm, bit| bm << bit }
74
+ end
75
+ send :"#{attr}=", Integer(mask)
76
+ instance_variable_set :"@#{virt}", mask
77
+ end
78
+ end
79
+
80
+ def generate_query(attr, virt, cls)
81
+ respond_to? :"#{virt}?" and
82
+ raise MethodCollisionError, "method `#{virt}?` already exists"
83
+
84
+ define_method :"#{virt}?" do |*bits|
85
+ cls.new(send(attr)).include? *bits
52
86
  end
53
87
  end
54
88
  end
@@ -1,3 +1,3 @@
1
1
  class BanditMask
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: banditmask
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Parker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-07-06 00:00:00.000000000 Z
11
+ date: 2015-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler