enums 1.2.0 → 1.3.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: 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