enums 1.2.0 → 1.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: c0e37d71cacfb8ee57b2b037caa082f6994912f6
4
- data.tar.gz: faf257d577a56cb4a0b1d0df423cef87dbda37ed
3
+ metadata.gz: 21d675f9ace380c191b6ee58c484ab192dbdb440
4
+ data.tar.gz: 42c0d89f1a150c48a432b3c2d1b1fbbe00ac1aa0
5
5
  SHA512:
6
- metadata.gz: 1f2faa8be748c71b7e2fe9c3dc93f0a31ce14ec4cc85211815bf4e6a27ef228525d649fc0d21bb340f725ab99eac317e2f2a2de9c85573707fc78ace22ac591f
7
- data.tar.gz: 54805411531355f799da430f9a6c243062f6676c9df2b0c92296e2a278f47511fb1b62f615b382db59f541cc286f6875ea3ba26989bf21006d4bdec04559e9e2
6
+ metadata.gz: f990e4ab5fcc741b1226a3f4eb0d8f9e53f8b2b446d1c8175a4f8bd3464d6fcde42a59cc966735bd2216abc4ec397fd3f043c89254815209c7c61fd0464459ba
7
+ data.tar.gz: 22ff3b44aea19efd814a45f3ad46184bfaf9df24bbb3e23bb306b91b9f068ff68381de7404293ecb47d5e8b8e6e8d669d92f184d2adcd36ff372740b2c35ce57
@@ -6,9 +6,9 @@ Rakefile
6
6
  lib/enums.rb
7
7
  lib/enums/enum.rb
8
8
  lib/enums/enum_builder.rb
9
- lib/enums/flags.rb
10
- lib/enums/flags_builder.rb
9
+ lib/enums/flag.rb
10
+ lib/enums/flag_builder.rb
11
11
  lib/enums/version.rb
12
12
  test/helper.rb
13
13
  test/test_enum.rb
14
- test/test_flags.rb
14
+ test/test_flag.rb
data/README.md CHANGED
@@ -49,6 +49,11 @@ Enum.new( :Color, :red, :green, :blue )
49
49
  enum :Color, :red, :green, :blue
50
50
  # or
51
51
  enum :Color, [:red, :green, :blue]
52
+ # or
53
+ enum :Color, { red: 0,
54
+ green: 1,
55
+ blue: 2 }
56
+
52
57
  ```
53
58
 
54
59
 
@@ -143,6 +148,10 @@ Enum.new( :State, :fundraising, :expired_refund, :successful )
143
148
  enum :State, :fundraising, :expired_refund, :successful
144
149
  # or
145
150
  enum :State, [:fundraising, :expired_refund, :successful]
151
+ # or
152
+ enum :State, { fundraising: 0,
153
+ expired_refund: 1,
154
+ successful: 2 }
146
155
 
147
156
 
148
157
  State.values #=> [0, 1, 2]
@@ -189,6 +198,187 @@ and so on.
189
198
 
190
199
 
191
200
 
201
+
202
+ ### What about enums with flags and bitwise-operators for set (`|`) / unset (`&~`) / toggle (`^`)?
203
+
204
+ Use the `flags` option or the `Flag` class. Example:
205
+
206
+ ``` ruby
207
+ Flag.new( :FileAttrib, :read_only, :hidden, :system, :archive )
208
+ # -or -
209
+ enum :FileAttrib, [:read_only, :hidden, :system, :archive], flags: true
210
+ # -or -
211
+ enum :FileAttrib, :read_only, :hidden, :system, :archive, flags: true
212
+ # -or -
213
+ enum :FileAttrib, { read_only: 1<<0, # 2^0 = 1 = 0b00000001
214
+ hidden: 1<<1, # 2^1 = 2 = 0b00000010
215
+ system: 1<<2, # 2^2 = 4 = 0b00000100
216
+ archive: 1<<5, # 2^5 = 32 = 0b00100000
217
+ },
218
+ flags: true
219
+ ```
220
+
221
+ (Auto-)builds a class and code like:
222
+
223
+ ``` ruby
224
+ class Flag
225
+ def initialize( key, value )
226
+ @key = key
227
+ @value = value
228
+ end
229
+ end
230
+
231
+
232
+ class FileAttrib < Flag
233
+
234
+ READ_ONLY = new( :read_only, 1<<0 )
235
+ HIDDEN = new( :hidden, 1<<1 )
236
+ SYSTEM = new( :system, 1<<2 )
237
+ ARCHIVE = new( :archive, 1<<3 )
238
+
239
+ def self.read_only() READ_ONLY; end
240
+ def self.hidden() HIDDEN; end
241
+ def self.system() SYSTEM; end
242
+ def self.archive() ARCHIVE; end
243
+
244
+ def self.values() [1<<0,1<<1,1<<2,1<<3]; end
245
+ def self.keys() [:read_only, :hidden, :system, :archive]; end
246
+ def self.members() [READ_ONLY, HIDDEN, SYSTEM, ARCHIVE]; end
247
+
248
+ def self.zero() @zero ||= new(0); end
249
+
250
+ def self.key( key )
251
+ @hash_by_key ||= Hash[ keys.zip( members ) ]
252
+ @hash_by_key[ key ]
253
+ end
254
+ def self.[]( key ) self.key( key ); end
255
+
256
+ def read_only?() member?( READ_ONLY ); end
257
+ def hidden?() member?( HIDDEN ); end
258
+ def system?() member?( SYSTEM ); end
259
+ def archive?() member?( ARCHIVE ); end
260
+
261
+ def member?( other ) @value & other.value == other.value; end
262
+
263
+ def bitwise_or( other )
264
+ self.class.new( @value | other.value )
265
+ end
266
+ alias_method :|, :bitwise_or
267
+ alias_method :set, :bitwise_or
268
+ alias_method :flag, :bitwise_or
269
+
270
+ def bitwise_and( other )
271
+ self.class.new( @value & other.value )
272
+ end
273
+ alias_method :&, :bitwise_and
274
+
275
+ def unset( other )
276
+ self.class.new( @value & ~other.value )
277
+ end
278
+ alias_method :unflag, :unset
279
+
280
+ def bitwise_xor( other )
281
+ self.class.new( @value ^ other.value )
282
+ end
283
+ alias_method :^, :bitwise_xor
284
+ alias_method :toggle, :bitwise_xor
285
+
286
+ # ...
287
+
288
+ def initialize( *args )
289
+ # ...
290
+ end
291
+ end
292
+
293
+
294
+ def FileAttrib( *args )
295
+ FileAttrib.new( *args )
296
+ end
297
+ ```
298
+
299
+ Use like:
300
+
301
+ ``` ruby
302
+ FileAttrib.values #=> [1, 2, 4, 8]
303
+ FileAttrib.keys #=> [:read_only, :hidden, :system, :archive]
304
+
305
+ FileAttrib.read_only #=> <FileAttrib @key=:read_only, @value=1>
306
+ FileAttrib::READ_ONLY #=> <FileAttrib @key=:read_only, @value=1>
307
+ FileAttribs[:read_only] #=> <FileAttrib @key=:read_only, @value=1>
308
+
309
+ FileAttrib(0) #=> <FileAttrib @key=:0000, @value=0>
310
+ FileAttrib.read_only | FileAttrib.hidden #=> <FileAttrib @key=:0011, @value=3>
311
+ # -or-
312
+ FileAttrib.new( FileAttrib.read_only | FileAttrib.hidden )
313
+ FileAttrib.new( FileAttrib::READ_ONLY | FileAttrib::HIDDEN )
314
+ FileAttrib.new( :read_only, :hidden )
315
+ # -or-
316
+ FileAttrib( FileAttrib.read_only | FileAttrib.hidden )
317
+ FileAttrib( FileAttrib::READ_ONLY | FileAttrib::HIDDEN )
318
+ FileAttrib( :read_only, :hidden )
319
+ #=> <FileAttrib @key=:0011, @value=3>
320
+
321
+ attrib = FileAttrib.new #=> <FileAttrib @key=:0000, @value=0>
322
+ attrib |= FileAttrib.read_only #=> <FileAttrib @key=:0001, @value=1>
323
+ attrib.read_only? #=> true
324
+ # -or-
325
+ attrib.member?( FileAttrib.read_only ) #=> true
326
+ attrib.member?( FileAttrib.READ_ONLY ) #=> true
327
+ attrib.member?( :read_only ) #=> true
328
+ attrib & FileAttrib.read_only == FileAttrib.read_only #=> true
329
+
330
+ attrib ^= FileAttrib.read_only #=> <FileAttrib @key=:0000, @value=0>
331
+ attrib.read_only? #=> false
332
+ attrib ^= FileAttrib.read_only #=> <FileAttrib @key=:0001, @value=1>
333
+ attrib.read_only? #=> true
334
+
335
+ attrib &= ~FileAttrib.read_only #=> <FileAttrib @key=:0000, @value=0>
336
+ attrib.read_only? #=> false
337
+
338
+ attrib.is_a? Flag #=> true
339
+ attrib.is_a? FileAttrib #=> true
340
+ # ...
341
+ ```
342
+
343
+ and so on.
344
+
345
+
346
+ ### What about enums for (algebraic) union data types with variants?
347
+
348
+ Yes, yes, yes. Use the `Union` class or the `data` helper from the safedata library.
349
+ Note, now you can go "wild" and use strings, arrays or really anything or nothing (that is, unit types) for your values. Example:
350
+
351
+ ``` ruby
352
+ data :Color, :Red, [1],
353
+ :Green, [2],
354
+ :Blue, [3]
355
+
356
+ # -or-
357
+
358
+ data :Color, :Red, ['red'],
359
+ :Green, ['green'],
360
+ :Blue, ['blue']
361
+
362
+ # -or-
363
+
364
+ data :Color, :Red, [1,'red'],
365
+ :Green, [2, 'green'],
366
+ :Blue, [3, 'blue']
367
+
368
+ # -or-
369
+
370
+ data :Color, :Red, [255, 0, 0],
371
+ :Green, [0, 255, 0],
372
+ :Blue, [0, 0, 255],
373
+ :Other, [:r, :g, :b]
374
+ ```
375
+
376
+ and so on and so forth.
377
+ See the [safedata library documentation for more »](https://github.com/s6ruby/safedata)
378
+
379
+
380
+
381
+
192
382
  ## More "Real World" Enum Samples
193
383
 
194
384
  - [The "Red Paper" about sruby](https://github.com/s6ruby/redpaper) - Small, Smart, Secure, Safe, Solid & Sound (S6) Ruby - The Ruby Programming Language for Contract / Transaction Scripts on the Blockchain World Computer - Yes, It's Just Ruby
@@ -29,15 +29,20 @@ end # module Safe
29
29
 
30
30
  require 'enums/enum'
31
31
  require 'enums/enum_builder'
32
- require 'enums/flags'
33
- require 'enums/flags_builder'
32
+ require 'enums/flag'
33
+ require 'enums/flag_builder'
34
34
 
35
35
 
36
36
 
37
37
 
38
38
  module Safe
39
+
40
+ ## note make Flags an alias of Flag
41
+ Flags = Flag
42
+
43
+
39
44
  module SafeHelper
40
- def enum( class_name, *args, flags: false, options: {} )
45
+ def enum( class_name, *args, flags: false, options: {}, **kwargs )
41
46
 
42
47
  ## note: allow "standalone" option flags or
43
48
  ## option hash
@@ -50,16 +55,14 @@ module SafeHelper
50
55
  # enum :Color, :red, :green, :blue
51
56
  # -or-
52
57
  # enum :Color, [:red, :green, :blue]
53
- if args[0].is_a?( Array )
54
- keys = args[0]
55
- else
56
- keys = args
58
+ if args.size > 0 && args[0].is_a?( Array )
59
+ args = args[0]
57
60
  end
58
61
 
59
62
  if options[:flags]
60
- Flags.new( class_name, *keys )
63
+ Flag.new( class_name, *args, **kwargs )
61
64
  else
62
- Enum.new( class_name, *keys )
65
+ Enum.new( class_name, *args, **kwargs )
63
66
  end
64
67
  end
65
68
  end # module SafeHelper
@@ -47,6 +47,10 @@ class Enum
47
47
  end
48
48
 
49
49
 
50
+ def self.zero() members[0]; end
51
+ def zero?() self == self.class.zero; end
52
+
53
+
50
54
  def self.size() keys.size; end
51
55
  def self.length() size; end ## alias (as is the ruby tradition)
52
56
 
@@ -58,8 +62,5 @@ class Enum
58
62
  ## note: will ALWAYS look-up by (member) index and NOT by value (integer number value might be different!!)
59
63
  members[ arg ]
60
64
  end
61
-
62
- def self.zero() members[0]; end
63
- def zero?() self == self.class.zero; end
64
65
  end # class Enum
65
66
  end # module Safe
@@ -7,13 +7,23 @@ class Enum
7
7
 
8
8
  ###################
9
9
  ## meta-programming "macro" - build class (on the fly)
10
- def self.build_class( class_name, *keys )
10
+ def self.build_class( class_name, *args, **kwargs )
11
+
12
+ if args.size > 0
13
+ keys = args
14
+ values = (0...keys.size).to_a # note: use ... (exclusive) range
15
+ e = Hash[ keys.zip( values ) ]
16
+ else
17
+ ## assume kwargs
18
+ e = kwargs
19
+ end
20
+
11
21
 
12
22
  ## todo/fix:
13
23
  ## check class name MUST start with uppercase letter
14
24
 
15
25
  ## check if all keys are symbols and follow the ruby id(entifier) naming rules
16
- keys.each do |key|
26
+ e.keys.each do |key|
17
27
  if key.is_a?( Symbol ) && key =~ /\A[a-z][a-zA-Z0-9_]*\z/
18
28
  else
19
29
  raise ArgumentError.new( "[Enum] arguments to Enum.new must be all symbols following the ruby id naming rules; >#{key}< failed" )
@@ -23,13 +33,13 @@ def self.build_class( class_name, *keys )
23
33
  klass = Class.new( Enum )
24
34
 
25
35
  ## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
26
- klass.define_singleton_method( :new ) do |*args|
27
- old_new( *args )
36
+ klass.define_singleton_method( :new ) do |*new_args|
37
+ old_new( *new_args )
28
38
  end
29
39
 
30
- keys.each_with_index do |key,index|
40
+ e.each do |key,value|
31
41
  klass.class_eval( <<RUBY )
32
- #{key.upcase} = new( :#{key}, #{index} )
42
+ #{key.upcase} = new( :#{key}, #{value} )
33
43
 
34
44
  def #{key}?
35
45
  self == #{key.upcase}
@@ -43,7 +53,7 @@ RUBY
43
53
 
44
54
  klass.class_eval( <<RUBY )
45
55
  def self.members
46
- @members ||= [#{keys.map {|key|key.upcase}.join(',')}].freeze
56
+ @members ||= [#{e.keys.map {|key|key.upcase}.join(',')}].freeze
47
57
  end
48
58
  RUBY
49
59
 
@@ -0,0 +1,147 @@
1
+ # encoding: utf-8
2
+
3
+ ###############################
4
+ ## base class for flag/flags
5
+
6
+
7
+
8
+ module Safe
9
+ class Flag
10
+ attr_reader :key
11
+ attr_reader :value
12
+
13
+ def initialize( *args )
14
+ fmt = "%08b"
15
+
16
+ if args.size == 0
17
+ @value = 0 ## same as new(0)
18
+ @key = :"#{fmt % @value}" ## use :none for 0 key - why? why not?
19
+ elsif args.size == 1 && args[0].is_a?(Integer)
20
+ @value = args[0]
21
+ @key = :"#{fmt % @value}" ## todo: lookup if value is a known flag (with key) - why? why not?
22
+ elsif args.size == 2 && args[0].is_a?(Symbol) && args[1].is_a?(Integer)
23
+ @key = args[0]
24
+ @value = args[1]
25
+ else
26
+ ## assume flag object or symbols
27
+ @value = 0
28
+ args.each do |arg|
29
+ flag = _typecast_flag!( arg )
30
+ @value |= flag.value
31
+ end
32
+ @key = :"#{fmt % @value}" ## todo: lookup if value is a known flag (with key) - why? why not?
33
+ end
34
+ self.freeze ## make "immutable" - should be a value object like an integer number!!!
35
+ self # return self for chaining
36
+ end
37
+
38
+
39
+ def _typecheck_flag!( o )
40
+ if self.class == o.class
41
+ o
42
+ else
43
+ raise TypeError.new( "[Flag] flag >#{self.class.name}< type expected; got >#{o.class.inspect}<" )
44
+ end
45
+ end
46
+
47
+ def _typecast_flag!( o )
48
+ if o.is_a? Symbol ## auto-convert symbol to flag
49
+ o = self.class.key( o ) ## lookup symbol in "parent" flags class
50
+ end
51
+ _typecheck_flag!( o )
52
+ end
53
+
54
+
55
+ def ==( other )
56
+ if self.class == other.class
57
+ @value == other.value
58
+ elsif other.is_a?( Integer ) ## note: also allow compare by "plain" integer
59
+ @value == other
60
+ else
61
+ false
62
+ end
63
+ end
64
+ alias_method :eql?, :==
65
+
66
+
67
+ def member?( other ) _member?(_typecast_flag!( other )); end
68
+ def _member?( other ) @value & other.value == other.value; end
69
+
70
+
71
+ def bitwise_or( other )
72
+ self.class.new( @value | _typecheck_flag!( other ).value )
73
+ end
74
+ alias_method :|, :bitwise_or
75
+
76
+ def bitwise_and( other )
77
+ self.class.new( @value & _typecheck_flag!( other ).value )
78
+ end
79
+ alias_method :&, :bitwise_and
80
+
81
+ def bitwise_xor( other )
82
+ self.class.new( @value ^ _typecheck_flag!( other ).value )
83
+ end
84
+ alias_method :^, :bitwise_xor
85
+
86
+ def bitwise_inverse
87
+ self.class.new( ~@value )
88
+ end
89
+ alias_method :~, :bitwise_inverse
90
+
91
+
92
+ def set( other ) ## note: typecast also allows symbol e.g (:read_only, etc.)
93
+ self.class.new( @value | _typecast_flag!( other ).value )
94
+ end
95
+ alias_method :flag, :set
96
+
97
+ def unset( other )
98
+ self.class.new( @value & ~_typecast_flag!( other ).value )
99
+ end
100
+ alias_method :unflag, :unset
101
+
102
+ def toggle( other )
103
+ self.class.new( @value ^ _typecast_flag!( other ).value )
104
+ end
105
+
106
+
107
+
108
+
109
+ def self.keys()
110
+ # note: does NOT include none - why? why not?
111
+ @keys ||= members.map { |member| member.key }.freeze
112
+ end
113
+
114
+ def self.key( key )
115
+ @hash ||= Hash[ keys.zip( members ) ].freeze
116
+ @hash[key]
117
+ end
118
+
119
+ def self.[]( key ) ## convenience alias for key
120
+ self.key( key )
121
+ end
122
+
123
+
124
+ def self.values()
125
+ # note: does NOT include none - why? why not?
126
+ @values ||= members.map { |member| member.value }.freeze
127
+ end
128
+
129
+
130
+ def self.zero() @zero ||= new(0); end
131
+ def zero?() @value == 0; end
132
+
133
+
134
+ def self.convert( *args )
135
+ new( *args )
136
+ end
137
+
138
+
139
+ ## add size|length too why? why not?
140
+ ## add value() lookup?
141
+ ## not for now - why? combined values are undefined!! what to return??
142
+
143
+ ## add to_i, to_int - why? why not?
144
+ ## def to_i() @value; end
145
+ ## def to_int() @value; end
146
+ end # class Flag
147
+ end # module Safe
@@ -1,37 +1,43 @@
1
1
  module Safe
2
- class Flags
2
+ class Flag
3
3
 
4
4
 
5
5
  ###################
6
6
  ## meta-programming "macro" - build class (on the fly)
7
- def self.build_class( class_name, *keys )
7
+ def self.build_class( class_name, *args, **kwargs )
8
+
9
+ if args.size > 0
10
+ keys = args
11
+ values = (0...keys.size).to_a # note: use ... (exclusive) range
12
+ values = values.map { |value| 1<<value } ## use power of twos (e.g. 2^0, 2^1, etc.)
13
+ f = Hash[ keys.zip( values ) ]
14
+ else
15
+ ## assume kwargs
16
+ f = kwargs
17
+ end
18
+
8
19
 
9
20
  ## todo/fix:
10
21
  ## check class name MUST start with uppercase letter
11
22
 
12
23
  ## check if all keys are symbols and follow the ruby id(entifier) naming rules
13
- keys.each do |key|
24
+ f.keys.each do |key|
14
25
  if key.is_a?( Symbol ) && key =~ /\A[a-z][a-zA-Z0-9_]*\z/
15
26
  else
16
- raise ArgumentError.new( "[Flags] arguments to Flags.new must be all symbols following the ruby id naming rules; >#{key}< failed" )
27
+ raise ArgumentError.new( "[Flag] arguments to Flag.new must be all symbols following the ruby id naming rules; >#{key}< failed" )
17
28
  end
18
29
  end
19
30
 
20
- klass = Class.new( Flags )
31
+ klass = Class.new( Flag )
21
32
  ## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
22
- klass.define_singleton_method( :new ) do |*args|
23
- old_new( *args )
33
+ klass.define_singleton_method( :new ) do |*new_args|
34
+ old_new( *new_args )
24
35
  end
25
36
 
26
37
 
27
- ## add nested flag class for "typesafe" flags
28
- klass.class_eval( <<RUBY )
29
- class Flag < Safe::Flag; end
30
- RUBY
31
-
32
- keys.each_with_index do |key,index|
38
+ f.each do |key,value|
33
39
  klass.class_eval( <<RUBY )
34
- #{key.upcase} = Flag.new( :#{key}, 1<<#{index} )
40
+ #{key.upcase} = new( :#{key}, #{value} )
35
41
 
36
42
  def self.#{key}
37
43
  #{key.upcase}
@@ -45,7 +51,7 @@ RUBY
45
51
 
46
52
  klass.class_eval( <<RUBY )
47
53
  def self.members
48
- @members ||= [#{keys.map {|key|key.upcase}.join(',')}].freeze
54
+ @members ||= [#{f.keys.map {|key|key.upcase}.join(',')}].freeze
49
55
  end
50
56
  RUBY
51
57
 
@@ -73,5 +79,5 @@ class << self
73
79
  alias_method :new, :build_class # replace original version with build_class
74
80
  end
75
81
 
76
- end # class Flags
82
+ end # class Flag
77
83
  end # module Safe
@@ -5,7 +5,7 @@
5
5
  module Enums
6
6
 
7
7
  MAJOR = 1
8
- MINOR = 2
8
+ MINOR = 3
9
9
  PATCH = 0
10
10
  VERSION = [MAJOR,MINOR,PATCH].join('.')
11
11
 
@@ -32,6 +32,10 @@ class TestEnum < MiniTest::Test
32
32
  pp Color
33
33
  pp Color(0)
34
34
 
35
+ enum :Roman, {i: 1, iv: 4, v: 5, ix: 9, x: 10 }
36
+ pp Roman
37
+ pp Roman(0) ## first member by index
38
+
35
39
 
36
40
  def test_state
37
41
  assert_equal [:FUNDRAISING, :EXPIRED_REFUND, :SUCCESSFUL], State.constants
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_flag.rb
6
+
7
+
8
+ require 'helper'
9
+
10
+
11
+ module Safe
12
+ ## Flags.new( :FileAttrib, :read_only, :hidden, :system, :archive )
13
+ enum :FileAttrib, [:read_only, :hidden, :system, :archive], flags: true
14
+
15
+
16
+ pp FileAttrib
17
+ pp FileAttrib(0)
18
+
19
+ puts "Safe.constants:"
20
+ pp Safe.constants #=> [:SafeHelper, :Enum, :State, :Color]
21
+ puts "Flag.constants:"
22
+ pp Flag.constants #=> []
23
+ puts "FileAttrib.constants:"
24
+ pp FileAttrib.constants #=> []
25
+ end
26
+
27
+
28
+ class TestFlag < MiniTest::Test
29
+
30
+ include Safe ## make all enums (and "convenience" converters) global
31
+
32
+
33
+ def test_attrib
34
+
35
+ assert_equal [1<<0, 1<<1, 1<<2, 1<<3], FileAttrib.values
36
+ assert_equal [:read_only, :hidden, :system, :archive], FileAttrib.keys
37
+
38
+ assert_equal FileAttrib.read_only, FileAttrib::READ_ONLY
39
+ assert_equal FileAttrib.hidden, FileAttrib::HIDDEN
40
+ assert_equal FileAttrib.system, FileAttrib::SYSTEM
41
+ assert_equal FileAttrib.archive, FileAttrib::ARCHIVE
42
+
43
+ assert_equal FileAttrib.read_only, FileAttrib[:read_only]
44
+ assert_equal FileAttrib.hidden, FileAttrib[:hidden]
45
+ assert_equal FileAttrib.system, FileAttrib[:system]
46
+ assert_equal FileAttrib.archive, FileAttrib[:archive]
47
+
48
+ assert_equal 1<<0, FileAttrib.read_only.value
49
+ assert_equal :read_only, FileAttrib.read_only.key
50
+ assert_equal true, FileAttrib.read_only.is_a?( Flag )
51
+ assert_equal true, FileAttrib.read_only.is_a?( FileAttrib )
52
+
53
+ pp FileAttrib.members
54
+ assert_equal :read_only, FileAttrib.members[0].key
55
+ assert_equal 1<<0, FileAttrib.members[0].value
56
+ assert_equal :hidden, FileAttrib.members[1].key
57
+ assert_equal 1<<1, FileAttrib.members[1].value
58
+
59
+ assert_equal 1<<0, FileAttrib.read_only.value
60
+ assert_equal :read_only, FileAttrib.read_only.key
61
+ assert_equal 1<<0, FileAttrib::READ_ONLY.value
62
+ assert_equal :read_only, FileAttrib::READ_ONLY.key
63
+ end
64
+
65
+ def test_text_style
66
+ Flag.new( :TextStyle, :bold, :italic, :underline )
67
+
68
+ assert_equal [1<<0, 1<<1, 1<<2], TextStyle.values
69
+ assert_equal [:bold, :italic, :underline], TextStyle.keys
70
+
71
+ style = TextStyle(0)
72
+ assert_equal true, style == 0
73
+ style |= TextStyle.bold
74
+ assert_equal true, style.member?( :bold )
75
+ assert_equal true, style.member?( TextStyle.bold )
76
+ assert_equal true, style & TextStyle.bold == TextStyle.bold
77
+ assert_equal true, style & TextStyle.bold != 0
78
+
79
+ assert_equal false, style.member?( :italic )
80
+ assert_equal false, style.member?( TextStyle.italic )
81
+ style |= TextStyle.italic
82
+ assert_equal true, style.member?( :italic )
83
+ assert_equal true, style.member?( TextStyle.italic )
84
+ assert_equal true, style & TextStyle.italic == TextStyle.italic
85
+ assert_equal true, style & TextStyle.italic != 0
86
+ end
87
+
88
+
89
+ end # class TestFlag
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enums
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-27 00:00:00.000000000 Z
11
+ date: 2019-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc
@@ -57,12 +57,12 @@ files:
57
57
  - lib/enums.rb
58
58
  - lib/enums/enum.rb
59
59
  - lib/enums/enum_builder.rb
60
- - lib/enums/flags.rb
61
- - lib/enums/flags_builder.rb
60
+ - lib/enums/flag.rb
61
+ - lib/enums/flag_builder.rb
62
62
  - lib/enums/version.rb
63
63
  - test/helper.rb
64
64
  - test/test_enum.rb
65
- - test/test_flags.rb
65
+ - test/test_flag.rb
66
66
  homepage: https://github.com/s6ruby/enums
67
67
  licenses:
68
68
  - Public Domain
@@ -1,193 +0,0 @@
1
- # encoding: utf-8
2
-
3
- ###############################
4
- ## base class for flag/flags
5
-
6
-
7
-
8
- module Safe
9
- class Flag
10
-
11
- attr_reader :key
12
- attr_reader :value
13
-
14
- def initialize( key, value )
15
- @key = key
16
- @value = value
17
- self.freeze ## make "immutable"
18
- self
19
- end
20
-
21
-
22
- def self._typecheck!( o )
23
- if self == o.class
24
- o
25
- else
26
- raise TypeError.new( "[Flag] flag >#{name}< type expected; got >#{o.class.inspect}<" )
27
- end
28
- end
29
-
30
- def self._value!( o )
31
- if o.is_a? Integer
32
- o
33
- else
34
- _typecheck!( o )
35
- o.value
36
- end
37
- end
38
-
39
- def bitwise_or( other ) ## bitwise-or
40
- ## note: returns "plain" integer
41
- @value | self.class._value!( other )
42
- end
43
- alias_method :|, :bitwise_or
44
-
45
- def bitwise_inverse() ## bitwise-inverse
46
- ## note: returns "plain" integer
47
- ~@value
48
- end
49
- alias_method :~, :bitwise_inverse
50
-
51
-
52
- def coerce( other )
53
- puts "[Flag] coerce( self= >#{self.inspect}<, other= >#{other.inspect}< #{other.class.name} )"
54
- if other.is_a?( Integer )
55
- [other, @value]
56
- else
57
- raise TypeError.new( "[Flag] coerce - wrong type >#{other.inspect}< #{other.class.name} - Integer number expected" )
58
- end
59
- end
60
- end # class Flag
61
-
62
-
63
-
64
- class Flags
65
- attr_reader :value
66
-
67
- def initialize( *args )
68
- if args.size == 0
69
- @value = 0 ## same as new(0)
70
- elsif args.size == 1 && args[0].is_a?(Integer)
71
- @value = args[0]
72
- else
73
- ## assume flag object or symbols
74
- @value = 0
75
- args.each do |arg|
76
- flag = _typecast_flag!( arg )
77
- @value |= flag.value
78
- end
79
- end
80
- self.freeze ## make "immutable" - should be a value object like an integer number!!!
81
- self # return self for chaining
82
- end
83
-
84
-
85
- def _value_for_flag!( o )
86
- self.class::Flag._value!( o )
87
- end
88
-
89
- def _typecheck_flag!( o )
90
- self.class::Flag._typecheck!( o )
91
- end
92
-
93
- def _typecast_flag!( o )
94
- if o.is_a? Symbol ## auto-convert symbol to flag
95
- o = self.class.key( o ) ## lookup symbol in "parent" flags class
96
- end
97
- _typecheck_flag!( o )
98
- end
99
-
100
-
101
- def member?( other ) _member?(_typecast_flag!( other )); end
102
- def _member?( other ) @value & other.value != 0; end
103
-
104
-
105
- def bitwise_or( other )
106
- _unsafe_bitwise_or( _value_for_flag!( other ) )
107
- end
108
- alias_method :|, :bitwise_or
109
-
110
- def _unsafe_bitwise_or( other ) ## always assumes other is an integer
111
- self.class.new( @value | other )
112
- end
113
-
114
- def set( other ) ## typesafe version of bitwise-or (|=) (no "plain" Integer allowed)
115
- _unsafe_bitwise_or( _typecast_flag!( other ).value )
116
- end
117
- alias_method :flag, :set
118
-
119
-
120
- def bitwise_and( other )
121
- _unsafe_bitwise_and( _value_for_flag!( other ) )
122
- end
123
- alias_method :&, :bitwise_and
124
-
125
- def _unsafe_bitwise_and( other )
126
- self.class.new( @value & other )
127
- end
128
-
129
-
130
- def unset( other ) ## typesafe version of bitwise-and/bitwise-inverse (&=~) (no "plain" Integer allowed)
131
- _unsafe_bitwise_and( ~_typecast_flag!( other ).value )
132
- end
133
- alias_method :unflag, :unset
134
-
135
-
136
- def bitwise_xor( other )
137
- _unsafe_bitwise_xor( _value_for_flag!( other ))
138
- end
139
- alias_method :^, :bitwise_xor
140
-
141
- def _unsafe_bitwise_xor( other )
142
- self.class.new( @value ^ other )
143
- end
144
-
145
- def toggle( other ) ## typesafe version of bitwise-xor (^=) (no "plain" Integer allowed)
146
- _unsafe_bitwise_xor( _typecast_flag!( other ).value )
147
- end
148
-
149
-
150
-
151
- def self.keys()
152
- # note: does NOT include none - why? why not?
153
- @keys ||= members.map { |member| member.key }.freeze
154
- end
155
-
156
- def self.key( key )
157
- @hash_by_key ||= Hash[ keys.zip( members ) ].freeze
158
- @hash_by_key[key]
159
- end
160
-
161
- def self.[]( key ) ## convenience alias for key
162
- self.key( key )
163
- end
164
-
165
-
166
- def self.values()
167
- # note: does NOT include none - why? why not?
168
- @values ||= members.map { |member| member.value }.freeze
169
- end
170
-
171
-
172
- def self.convert( *args )
173
- new( *args )
174
- end
175
-
176
- def self.zero() @zero ||= new(0); end
177
- def zero?() @value == 0; end
178
-
179
- ### todo/fix:
180
- ## add ==/eql?
181
- ## for self AND flag AND integer
182
- ## always compare integer value
183
-
184
-
185
- ## add size|length too why? why not?
186
- ## add value() lookup?
187
- ## not for now - why? combined values are undefined!! what to return??
188
-
189
- ## add to_i, to_int - why? why not?
190
- ## def to_i() @value; end
191
- ## def to_int() @value; end
192
- end # class Flags
193
- end # module Safe
@@ -1,68 +0,0 @@
1
- # encoding: utf-8
2
-
3
- ##
4
- # to run use
5
- # ruby -I ./lib -I ./test test/test_flags.rb
6
-
7
-
8
- require 'helper'
9
-
10
-
11
- module Safe
12
- ## Flags.new( :FileAttribs, :readonly, :hidden, :system, :archive )
13
- enum :FileAttribs, [:readonly, :hidden, :system, :archive], flags: true
14
-
15
-
16
- pp FileAttribs
17
- pp FileAttribs::Flag
18
- pp FileAttribs(0)
19
-
20
- puts "Safe.constants:"
21
- pp Safe.constants #=> [:SafeHelper, :Enum, :State, :Color]
22
- puts "Flags.constants:"
23
- pp Flags.constants #=> []
24
- puts "Flag.constants:"
25
- pp Flag.constants #=> []
26
- puts "FileAttribs.constants:"
27
- pp FileAttribs.constants #=> []
28
- end
29
-
30
-
31
- class TestFlags < MiniTest::Test
32
-
33
- include Safe ## make all enums (and "convenience" converters) global
34
-
35
-
36
- def test_attribs
37
-
38
- assert_equal [1<<0, 1<<1, 1<<2, 1<<3], FileAttribs.values
39
- assert_equal [:readonly, :hidden, :system, :archive], FileAttribs.keys
40
-
41
- assert_equal FileAttribs.readonly, FileAttribs::READONLY
42
- assert_equal FileAttribs.hidden, FileAttribs::HIDDEN
43
- assert_equal FileAttribs.system, FileAttribs::SYSTEM
44
- assert_equal FileAttribs.archive, FileAttribs::ARCHIVE
45
-
46
- assert_equal FileAttribs.readonly, FileAttribs[:readonly]
47
- assert_equal FileAttribs.hidden, FileAttribs[:hidden]
48
- assert_equal FileAttribs.system, FileAttribs[:system]
49
- assert_equal FileAttribs.archive, FileAttribs[:archive]
50
-
51
- assert_equal 1<<0, FileAttribs.readonly.value
52
- assert_equal :readonly, FileAttribs.readonly.key
53
- assert_equal true, FileAttribs.readonly.is_a?( Flag )
54
- assert_equal true, FileAttribs.readonly.is_a?( FileAttribs::Flag )
55
-
56
- pp FileAttribs.members
57
- assert_equal :readonly, FileAttribs.members[0].key
58
- assert_equal 1<<0, FileAttribs.members[0].value
59
- assert_equal :hidden, FileAttribs.members[1].key
60
- assert_equal 1<<1, FileAttribs.members[1].value
61
-
62
- assert_equal 1<<0, FileAttribs.readonly.value
63
- assert_equal :readonly, FileAttribs.readonly.key
64
- assert_equal 1<<0, FileAttribs::READONLY.value
65
- assert_equal :readonly, FileAttribs::READONLY.key
66
- end
67
-
68
- end # class TestFlags