traits 0.9.1 → 0.10.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.
data/README CHANGED
@@ -3,19 +3,47 @@ URLS
3
3
  http://rubyforge.org/projects/codeforpeople/
4
4
  http://codeforpeople.com/lib/ruby/traits
5
5
 
6
+ INSTALL
7
+
8
+ yes|sudo gem install traits
9
+
6
10
  ABOUT
7
11
 
8
- traits.rb is set of attr_* like methods on steroids, caffeine, and botox. it
9
- encourages better living through meta-programming and uniform access
12
+ traits.rb is set of attr_* like methods on steroids, caffeine, and botox.
13
+ it encourages better living through meta-programming and uniform access
10
14
  priciples. traits.rb supports smart inheritence of class attributes and a
11
15
  fistful of hooks for veryifying and munging attr values.
12
16
 
13
17
  VERSION
14
18
 
15
- 0.9.1
19
+ 0.10.0
16
20
 
17
21
  HISTORY
18
22
 
23
+ 0.10.0
24
+ - removed use of additional instance var to denote whether a value had
25
+ been set. now the test is only 'defined? @#{ name }'.
26
+
27
+ 0.9.2
28
+ - fixed a bug where list traits, for example
29
+
30
+ trait 'letters' => %w[ a b c ]
31
+
32
+ were flattened in a way that exploded trait initialization
33
+
34
+ - streamlined TraitInit module
35
+
36
+ - added OpenTraits class and otraits method
37
+
38
+ conf = otraits{
39
+ port 42
40
+ host 'forty-two'
41
+ }
42
+ p conf.port #=> 42
43
+ p conf.host #=> 'forty-two'
44
+ conf.username 'zaphod'
45
+ p conf #=> {"username"=>"zaphod", "port"=>42, "host"=>"forty-two"}
46
+
19
47
  0.9.0
20
48
  - luke kaines made quite a few suggestions and bug reports that enabled this
21
49
  release including making a few methods indifferent about string/symbol
@@ -422,6 +450,7 @@ SAMPLES
422
450
  p r?
423
451
  self.r = 42
424
452
  p r
453
+ p r?
425
454
  end
426
455
  end
427
456
  C::new.using_private_writer_and_query
@@ -435,6 +464,7 @@ SAMPLES
435
464
  p w?
436
465
  self.w = 'forty-two'
437
466
  p w
467
+ p w?
438
468
  end
439
469
  end
440
470
  D::new.using_private_reader
@@ -443,8 +473,10 @@ SAMPLES
443
473
 
444
474
  false
445
475
  42
476
+ true
446
477
  false
447
478
  "forty-two"
479
+ true
448
480
 
449
481
 
450
482
  <========< sample/j.rb >========>
@@ -664,13 +696,50 @@ SAMPLES
664
696
  end
665
697
 
666
698
  c = C::new "li" => [4, 2], "ls" => %w[4 2]
667
- p c.li.join
668
- p c.ls.join
699
+ p c.li
700
+ p c.ls
669
701
 
670
702
  ~ > ruby sample/p.rb
671
703
 
672
- "42"
673
- "42"
704
+ [4, 2]
705
+ ["4", "2"]
706
+
707
+
708
+ <========< sample/q.rb >========>
709
+
710
+ ~ > cat sample/q.rb
711
+
712
+ require 'traits'
713
+ #
714
+ # the OpenTraits class is similar to an OpenStruct but, imho, easier to use.
715
+ # the otraits shorthand can be used to contruct one
716
+ #
717
+
718
+ #
719
+ # options passed as args dynamically create and init traits
720
+ #
721
+ config = otraits :port => 42
722
+ p config.port
723
+
724
+ #
725
+ # any passed block does the same but, via a method missing hood and traits
726
+ # getter/setters, the syntax is very clean
727
+ #
728
+ config = otraits{
729
+ port 42
730
+ host 'forty-two'
731
+ }
732
+ p config.port
733
+ p config.host
734
+ config.username 'zaphod'
735
+ p config
736
+
737
+ ~ > ruby sample/q.rb
738
+
739
+ 42
740
+ 42
741
+ "forty-two"
742
+ {"username"=>"zaphod", "port"=>42, "host"=>"forty-two"}
674
743
 
675
744
 
676
745
  CAVEATS
@@ -3,19 +3,47 @@ URLS
3
3
  http://rubyforge.org/projects/codeforpeople/
4
4
  http://codeforpeople.com/lib/ruby/traits
5
5
 
6
+ INSTALL
7
+
8
+ yes|sudo gem install traits
9
+
6
10
  ABOUT
7
11
 
8
- traits.rb is set of attr_* like methods on steroids, caffeine, and botox. it
9
- encourages better living through meta-programming and uniform access
12
+ traits.rb is set of attr_* like methods on steroids, caffeine, and botox.
13
+ it encourages better living through meta-programming and uniform access
10
14
  priciples. traits.rb supports smart inheritence of class attributes and a
11
15
  fistful of hooks for veryifying and munging attr values.
12
16
 
13
17
  VERSION
14
18
 
15
- 0.9.1
19
+ 0.10.0
16
20
 
17
21
  HISTORY
18
22
 
23
+ 0.10.0
24
+ - removed use of additional instance var to denote whether a value had
25
+ been set. now the test is only 'defined? @#{ name }'.
26
+
27
+ 0.9.2
28
+ - fixed a bug where list traits, for example
29
+
30
+ trait 'letters' => %w[ a b c ]
31
+
32
+ were flattened in a way that exploded trait initialization
33
+
34
+ - streamlined TraitInit module
35
+
36
+ - added OpenTraits class and otraits method
37
+
38
+ conf = otraits{
39
+ port 42
40
+ host 'forty-two'
41
+ }
42
+ p conf.port #=> 42
43
+ p conf.host #=> 'forty-two'
44
+ conf.username 'zaphod'
45
+ p conf #=> {"username"=>"zaphod", "port"=>42, "host"=>"forty-two"}
46
+
19
47
  0.9.0
20
48
  - luke kaines made quite a few suggestions and bug reports that enabled this
21
49
  release including making a few methods indifferent about string/symbol
@@ -19,7 +19,7 @@ Dir['sample/*'].each do |sample|
19
19
  cmd = "ruby #{ sample }"
20
20
  samples << indent(prompt + cmd, 2) << "\n\n"
21
21
 
22
- cmd = "ruby -I ./lib -r ./lib/traits.rb #{ sample }"
22
+ cmd = "ruby -I./lib/ #{ sample }"
23
23
  samples << indent(`#{ cmd } 2>&1`, 4) << "\n"
24
24
  end
25
25
 
@@ -1,5 +1,9 @@
1
- $__TRAIT_DEBUG__ = ENV['__TRAIT_DEBUG__'] || ENV['TRAIT_DEBUG'] || ENV['DEBUG']
2
- $__TRAIT_VERSION__ = "0.9.1"
1
+ module Traits
2
+ #--{{{
3
+ VERSION = "0.10.0"
4
+ DEBUG = ENV['__TRAIT_DEBUG__'] || ENV['TRAIT_DEBUG'] || ENV['DEBUG']
5
+ #--}}}
6
+ end
3
7
 
4
8
  class Object
5
9
  #--{{{
@@ -61,6 +65,7 @@ class Object
61
65
  ducktype = nil
62
66
  default = nil
63
67
  names_and_defaults = nil
68
+ toggle = nil
64
69
 
65
70
  if block and not block.respond_to? :__trait_default
66
71
  block.__trait_singleton_class.class_eval{ attr '__trait_default' }
@@ -77,6 +82,7 @@ class Object
77
82
  type = __trait_getopt(opts, 'type', __trait_getopt(opts, 'case'))
78
83
  ducktype = __trait_getopt opts, 'ducktype'
79
84
  default = __trait_getopt opts, 'default'
85
+ toggle = __trait_getopt opts, 'toggle'
80
86
 
81
87
  list, hashes = list.partition{|arg| not Hash === arg}
82
88
  hashes << opts unless opts.empty? # in which case it was not, in fact, opts
@@ -91,7 +97,9 @@ class Object
91
97
 
92
98
  # force list and names_and_defaults.keys to strings
93
99
  list = list.map{|t| "#{ t }"}
94
- names_and_defaults = Hash[ *names_and_defaults.to_a.map{|k,v| ["#{ k }", v]}.flatten ]
100
+ #names_and_defaults = Hash[ *names_and_defaults.to_a.map{|k,v| ["#{ k }", v]}.flatten ]
101
+ h = names_and_defaults
102
+ h.keys.each{|k| h[k.to_s] = h.delete(k)}
95
103
 
96
104
  list.each{|name| names_and_defaults[name] = default}
97
105
 
@@ -105,6 +113,7 @@ class Object
105
113
  'ducktype' => ducktype,
106
114
  'validate' => validate,
107
115
  'post' => post,
116
+ 'toggle' => toggle,
108
117
  }
109
118
 
110
119
  names_and_hooks = names.inject({}){|h, name| h.update name => hooks}
@@ -202,6 +211,8 @@ class Object
202
211
  reader_trait
203
212
  r_traits
204
213
  r_trait
214
+ rtraits
215
+ rtrait
205
216
  has_readers
206
217
  has_reader
207
218
  has_r
@@ -212,6 +223,8 @@ class Object
212
223
  writer_trait
213
224
  w_traits
214
225
  w_trait
226
+ wtraits
227
+ wtrait
215
228
  has_writers
216
229
  has_writer
217
230
  has_w
@@ -230,6 +243,7 @@ class Object
230
243
  end
231
244
  #--}}}
232
245
  end
246
+
233
247
  class Class
234
248
  #--{{{
235
249
  def __trait_singleton_super
@@ -240,6 +254,7 @@ class Class
240
254
  end
241
255
  #--}}}
242
256
  end
257
+
243
258
  class Module
244
259
  #--{{{
245
260
 
@@ -248,7 +263,7 @@ class Module
248
263
  begin
249
264
  module_eval(*a, &b)
250
265
  rescue Exception => e
251
- STDERR.puts([a, b].inspect) if $__TRAIT_DEBUG__
266
+ STDERR.puts([a, b].inspect) if Traits::DEBUG
252
267
  raise
253
268
  end
254
269
  #--}}}
@@ -388,6 +403,7 @@ class Module
388
403
  getter = "#{ name }"
389
404
  setter = "#{ name }="
390
405
  query = "#{ name }?"
406
+ toggle = "#{ name }!"
391
407
 
392
408
  defaults[getter] = default if default
393
409
  list['readers'] << getter
@@ -406,6 +422,10 @@ class Module
406
422
  code = __trait_gen_query_code name, 'public'
407
423
  __trait_module_eval code
408
424
  end
425
+ unless instance_methods.include? toggle
426
+ code = __trait_gen_toggle_code name, 'private'
427
+ __trait_module_eval code
428
+ end
409
429
  #--}}}
410
430
  end
411
431
 
@@ -414,6 +434,7 @@ class Module
414
434
  reader = "#{ name }"
415
435
  writer = "#{ name }="
416
436
  query = "#{ name }?"
437
+ toggle = "#{ name }!"
417
438
 
418
439
  defaults[reader] = default if default
419
440
  list['writers'] << writer
@@ -432,6 +453,10 @@ class Module
432
453
  code = __trait_gen_query_code name, 'private'
433
454
  __trait_module_eval code
434
455
  end
456
+ unless instance_methods.include? toggle
457
+ code = __trait_gen_toggle_code name, 'public'
458
+ __trait_module_eval code
459
+ end
435
460
  #--}}}
436
461
  end
437
462
 
@@ -454,7 +479,8 @@ class Module
454
479
  unless a.empty?
455
480
  send('#{ name }=', *a, &b)
456
481
  else
457
- unless(defined?(@________#{ name }_set) and @________#{ name }_set)
482
+ #unless(defined?(@________#{ name }_set) and @________#{ name }_set)
483
+ unless defined? @#{ name }
458
484
  #{ klass }::__trait_search_path.each do |obj|
459
485
  defaults = obj.#{ defaults }
460
486
  if defaults.has_key? '#{ name }'
@@ -485,7 +511,7 @@ class Module
485
511
  end
486
512
  #{ access_protection } '#{ name }'.intern
487
513
  code
488
- puts reader_code if $__TRAIT_DEBUG__
514
+ puts reader_code if Traits::DEBUG
489
515
  reader_code
490
516
  #--}}}
491
517
  end
@@ -601,7 +627,7 @@ class Module
601
627
 
602
628
  @#{ name } = value
603
629
 
604
- @________#{ name }_set = true
630
+ #@________#{ name }_set = true
605
631
 
606
632
  if post_hook
607
633
  [ post_hook ].flatten.each do |post|
@@ -631,7 +657,7 @@ class Module
631
657
  end
632
658
  #{ access_protection } '#{ name }='.intern
633
659
  code
634
- puts writer_code if $__TRAIT_DEBUG__
660
+ puts writer_code if Traits::DEBUG
635
661
  writer_code
636
662
  #--}}}
637
663
  end
@@ -644,11 +670,193 @@ class Module
644
670
  end
645
671
  # #{ access_protection } '#{ name }?'.intern
646
672
  code
647
- puts query_code if $__TRAIT_DEBUG__
673
+ puts query_code if Traits::DEBUG
648
674
  query_code
649
675
  #--}}}
650
676
  end
651
677
 
678
+ def __trait_gen_toggle_code name, access_protection = 'public'
679
+ #--{{{
680
+ s = __trait_singleton?
681
+ klass = s ? 'self' : 'self.class'
682
+ hooks = s ? '__trait_singleton_method_hooks' : '__trait_instance_method_hooks'
683
+
684
+ writer_code = <<-code
685
+ def #{ name }!
686
+ hooks = {}
687
+ hook_types = %w( pre munge cast type ducktype validate post toggle )
688
+
689
+ #{ klass }::__trait_search_path.each do |obj|
690
+
691
+ break if hooks.values_at(*hook_types).compact.size == hook_types.size
692
+
693
+ hook_types.each{|ht| hooks[ht] ||= obj.#{ hooks }['#{ name }'][ht]}
694
+
695
+ end
696
+
697
+ pre_hook, munge_hook, cast_hook, type_hook, ducktype_hook, validate_hook, post_hook, toggle_hook =
698
+ hooks.values_at(*hook_types)
699
+
700
+ toggle =
701
+ if defined? @________#{ name }_toggle
702
+ @________#{ name }_toggle
703
+ else
704
+ if toggle_hook
705
+ case toggle_hook
706
+ when Proc
707
+ t = Object.new
708
+ sc = class << t
709
+ self
710
+ end
711
+ sc.module_eval{
712
+ define_method 'shift' do
713
+ toggle_hook.call
714
+ end
715
+ define_method 'push' do
716
+ end
717
+ }
718
+ t
719
+ when Enumerable
720
+ toggle_hook.to_a
721
+ when NilClass, TrueClass
722
+ @________#{ name }_toggle = [true, false]
723
+ else
724
+ if toggle_hook.respond_to?('shift') and toggle_hook.respond_to?('push')
725
+ toggle_hook
726
+ else
727
+ raise "bad toggle <\#{ toggle_hook.inspect }>"
728
+ end
729
+ end
730
+ else
731
+ @________#{ name }_toggle = [true, false]
732
+ end
733
+ end
734
+
735
+ value = toggle.shift
736
+ toggle.push value
737
+
738
+ if pre_hook
739
+ [ pre_hook ].flatten.each do |pre|
740
+ if Proc === pre
741
+ case pre.arity
742
+ when 0
743
+ __trait_evaluate &pre
744
+ when 1
745
+ __trait_evaluate value, &pre
746
+ else
747
+ __trait_evaluate "#{ name }", value, &pre
748
+ end
749
+ else
750
+ case method("\#{ pre }").arity
751
+ when 0
752
+ send "\#{ pre }"
753
+ when 1
754
+ send "\#{ pre }", value
755
+ else
756
+ send "\#{ pre }", "#{ name }", value
757
+ end
758
+ end
759
+ end
760
+ end
761
+
762
+ if cast_hook
763
+ [ cast_hook ].flatten.each do |cast|
764
+ value =
765
+ if Proc === cast
766
+ __trait_evaluate value, &cast
767
+ else
768
+ send cast, value
769
+ end
770
+ end
771
+ end
772
+
773
+ if munge_hook
774
+ [ munge_hook ].flatten.each do |munge|
775
+ value =
776
+ if Proc === munge
777
+ __trait_evaluate value, &munge
778
+ else
779
+ value.send munge
780
+ end
781
+ end
782
+ end
783
+
784
+ if type_hook
785
+ [ type_hook ].flatten.each do |type|
786
+ is_valid =
787
+ if Proc === type
788
+ __trait_evaluate(value, &type) === value
789
+ else
790
+ type === value
791
+ end
792
+ raise ArgumentError,
793
+ "validation of <\#{ value.inspect }> failed!" unless is_valid
794
+ end
795
+ end
796
+
797
+ if ducktype_hook
798
+ [ ducktype_hook ].flatten.each do |ducktype|
799
+ is_valid =
800
+ if Proc === ducktype
801
+ value.respond_to? __trait_evaluate(value, &ducktype)
802
+ else
803
+ value.respond_to? ducktype
804
+ end
805
+ raise ArgumentError,
806
+ "validation of <\#{ value.inspect }> failed!" unless is_valid
807
+ end
808
+ end
809
+
810
+ if validate_hook
811
+ [ validate_hook ].flatten.each do |validate|
812
+ is_valid =
813
+ if Proc === validate
814
+ __trait_evaluate value, &validate
815
+ else
816
+ send "\#{ validate }", value
817
+ end
818
+ raise ArgumentError,
819
+ "validation of <\#{ value.inspect }> failed!" unless is_valid
820
+ end
821
+ end
822
+
823
+ @#{ name } = value
824
+
825
+ #@________#{ name }_set = true
826
+
827
+ if post_hook
828
+ [ post_hook ].flatten.each do |post|
829
+ if Proc === post
830
+ case post.arity
831
+ when 0
832
+ __trait_evaluate &post
833
+ when 1
834
+ __trait_evaluate value, &post
835
+ else
836
+ __trait_evaluate "#{ name }", value, &post
837
+ end
838
+ else
839
+ case method("\#{ post }").arity
840
+ when 0
841
+ send "\#{ post }"
842
+ when 1
843
+ send "\#{ post }", value
844
+ else
845
+ send "\#{ post }", "#{ name }", value
846
+ end
847
+ end
848
+ end
849
+ end
850
+
851
+ @#{ name }
852
+ end
853
+ #{ access_protection } '#{ name }='.intern
854
+ code
855
+ puts writer_code if Traits::DEBUG
856
+ writer_code
857
+ #--}}}
858
+ end
859
+
652
860
  def __trait_gen_access_protection_code name, access_protection = nil
653
861
  #--{{{
654
862
  access_protection ||= 'public'
@@ -661,7 +869,7 @@ class Module
661
869
  else
662
870
  "public '#{ name }'.intern"
663
871
  end
664
- puts access_protection_code if $__TRAIT_DEBUG__
872
+ puts access_protection_code if Traits::DEBUG
665
873
  access_protection_code
666
874
  #--}}}
667
875
  end
@@ -676,8 +884,8 @@ module TraitInit
676
884
  args, opts = argv.partition{|arg| not Hash === arg}
677
885
  args.flatten!
678
886
  opts = opts.inject({}){|h,h2| h.update h2}
679
- msgs = r_traits
680
- args.each{|arg| send msgs.shift, v}
887
+ msgs = self.class.r_traits
888
+ args.each{|arg| send msgs.shift, arg}
681
889
  opts.each do |k,v|
682
890
  k = "#{ k }"
683
891
  if respond_to? k
@@ -711,3 +919,44 @@ module TraitInit
711
919
  end
712
920
  #--}}}
713
921
  end
922
+
923
+ class OpenTraits
924
+ #--{{{
925
+ def initialize h = {}, &b
926
+ h.each{|k,v| trait k => v}
927
+ instance_eval &b if b
928
+ end
929
+
930
+ def method_missing m, *a, &b
931
+ m = m.to_s.delete '='
932
+ unless respond_to? m
933
+ if a.empty?
934
+ b ? trait(m, &b) : trait(m)
935
+ else
936
+ b ? trait(m => a.shift, &b) : trait(m => a.shift)
937
+ end
938
+ end
939
+ send m
940
+ end
941
+
942
+ def to_hash
943
+ rtraits.inject({}){|h,t| h.update t => send(t)}
944
+ end
945
+ alias_method "to_h", "to_hash"
946
+
947
+ def to_s *a, &b
948
+ to_hash.to_s *a, &b
949
+ end
950
+
951
+ def inspect *a, &b
952
+ to_hash.inspect *a, &b
953
+ end
954
+
955
+ def to_yaml *a, &b
956
+ to_hash.to_yaml *a, &b
957
+ end
958
+ #--}}}
959
+ end
960
+ def OpenTraits(*a, &b) OpenTraits::new(*a, &b) end
961
+ def opentraits(*a, &b) OpenTraits::new(*a, &b) end
962
+ def otraits(*a, &b) OpenTraits::new(*a, &b) end
@@ -1,5 +1,9 @@
1
- $__TRAIT_DEBUG__ = ENV['__TRAIT_DEBUG__'] || ENV['TRAIT_DEBUG'] || ENV['DEBUG']
2
- $__TRAIT_VERSION__ = "0.9.1"
1
+ module Traits
2
+ #--{{{
3
+ VERSION = "0.10.0"
4
+ DEBUG = ENV['__TRAIT_DEBUG__'] || ENV['TRAIT_DEBUG'] || ENV['DEBUG']
5
+ #--}}}
6
+ end
3
7
 
4
8
  class Object
5
9
  #--{{{
@@ -61,6 +65,7 @@ class Object
61
65
  ducktype = nil
62
66
  default = nil
63
67
  names_and_defaults = nil
68
+ toggle = nil
64
69
 
65
70
  if block and not block.respond_to? :__trait_default
66
71
  block.__trait_singleton_class.class_eval{ attr '__trait_default' }
@@ -77,6 +82,7 @@ class Object
77
82
  type = __trait_getopt(opts, 'type', __trait_getopt(opts, 'case'))
78
83
  ducktype = __trait_getopt opts, 'ducktype'
79
84
  default = __trait_getopt opts, 'default'
85
+ toggle = __trait_getopt opts, 'toggle'
80
86
 
81
87
  list, hashes = list.partition{|arg| not Hash === arg}
82
88
  hashes << opts unless opts.empty? # in which case it was not, in fact, opts
@@ -91,7 +97,9 @@ class Object
91
97
 
92
98
  # force list and names_and_defaults.keys to strings
93
99
  list = list.map{|t| "#{ t }"}
94
- names_and_defaults = Hash[ *names_and_defaults.to_a.map{|k,v| ["#{ k }", v]}.flatten ]
100
+ #names_and_defaults = Hash[ *names_and_defaults.to_a.map{|k,v| ["#{ k }", v]}.flatten ]
101
+ h = names_and_defaults
102
+ h.keys.each{|k| h[k.to_s] = h.delete(k)}
95
103
 
96
104
  list.each{|name| names_and_defaults[name] = default}
97
105
 
@@ -105,6 +113,7 @@ class Object
105
113
  'ducktype' => ducktype,
106
114
  'validate' => validate,
107
115
  'post' => post,
116
+ 'toggle' => toggle,
108
117
  }
109
118
 
110
119
  names_and_hooks = names.inject({}){|h, name| h.update name => hooks}
@@ -202,6 +211,8 @@ class Object
202
211
  reader_trait
203
212
  r_traits
204
213
  r_trait
214
+ rtraits
215
+ rtrait
205
216
  has_readers
206
217
  has_reader
207
218
  has_r
@@ -212,6 +223,8 @@ class Object
212
223
  writer_trait
213
224
  w_traits
214
225
  w_trait
226
+ wtraits
227
+ wtrait
215
228
  has_writers
216
229
  has_writer
217
230
  has_w
@@ -230,6 +243,7 @@ class Object
230
243
  end
231
244
  #--}}}
232
245
  end
246
+
233
247
  class Class
234
248
  #--{{{
235
249
  def __trait_singleton_super
@@ -240,6 +254,7 @@ class Class
240
254
  end
241
255
  #--}}}
242
256
  end
257
+
243
258
  class Module
244
259
  #--{{{
245
260
 
@@ -248,7 +263,7 @@ class Module
248
263
  begin
249
264
  module_eval(*a, &b)
250
265
  rescue Exception => e
251
- STDERR.puts([a, b].inspect) if $__TRAIT_DEBUG__
266
+ STDERR.puts([a, b].inspect) if Traits::DEBUG
252
267
  raise
253
268
  end
254
269
  #--}}}
@@ -388,6 +403,7 @@ class Module
388
403
  getter = "#{ name }"
389
404
  setter = "#{ name }="
390
405
  query = "#{ name }?"
406
+ toggle = "#{ name }!"
391
407
 
392
408
  defaults[getter] = default if default
393
409
  list['readers'] << getter
@@ -406,6 +422,10 @@ class Module
406
422
  code = __trait_gen_query_code name, 'public'
407
423
  __trait_module_eval code
408
424
  end
425
+ unless instance_methods.include? toggle
426
+ code = __trait_gen_toggle_code name, 'private'
427
+ __trait_module_eval code
428
+ end
409
429
  #--}}}
410
430
  end
411
431
 
@@ -414,6 +434,7 @@ class Module
414
434
  reader = "#{ name }"
415
435
  writer = "#{ name }="
416
436
  query = "#{ name }?"
437
+ toggle = "#{ name }!"
417
438
 
418
439
  defaults[reader] = default if default
419
440
  list['writers'] << writer
@@ -432,6 +453,10 @@ class Module
432
453
  code = __trait_gen_query_code name, 'private'
433
454
  __trait_module_eval code
434
455
  end
456
+ unless instance_methods.include? toggle
457
+ code = __trait_gen_toggle_code name, 'public'
458
+ __trait_module_eval code
459
+ end
435
460
  #--}}}
436
461
  end
437
462
 
@@ -454,7 +479,8 @@ class Module
454
479
  unless a.empty?
455
480
  send('#{ name }=', *a, &b)
456
481
  else
457
- unless(defined?(@________#{ name }_set) and @________#{ name }_set)
482
+ #unless(defined?(@________#{ name }_set) and @________#{ name }_set)
483
+ unless defined? @#{ name }
458
484
  #{ klass }::__trait_search_path.each do |obj|
459
485
  defaults = obj.#{ defaults }
460
486
  if defaults.has_key? '#{ name }'
@@ -485,7 +511,7 @@ class Module
485
511
  end
486
512
  #{ access_protection } '#{ name }'.intern
487
513
  code
488
- puts reader_code if $__TRAIT_DEBUG__
514
+ puts reader_code if Traits::DEBUG
489
515
  reader_code
490
516
  #--}}}
491
517
  end
@@ -601,7 +627,7 @@ class Module
601
627
 
602
628
  @#{ name } = value
603
629
 
604
- @________#{ name }_set = true
630
+ #@________#{ name }_set = true
605
631
 
606
632
  if post_hook
607
633
  [ post_hook ].flatten.each do |post|
@@ -631,7 +657,7 @@ class Module
631
657
  end
632
658
  #{ access_protection } '#{ name }='.intern
633
659
  code
634
- puts writer_code if $__TRAIT_DEBUG__
660
+ puts writer_code if Traits::DEBUG
635
661
  writer_code
636
662
  #--}}}
637
663
  end
@@ -644,11 +670,193 @@ class Module
644
670
  end
645
671
  # #{ access_protection } '#{ name }?'.intern
646
672
  code
647
- puts query_code if $__TRAIT_DEBUG__
673
+ puts query_code if Traits::DEBUG
648
674
  query_code
649
675
  #--}}}
650
676
  end
651
677
 
678
+ def __trait_gen_toggle_code name, access_protection = 'public'
679
+ #--{{{
680
+ s = __trait_singleton?
681
+ klass = s ? 'self' : 'self.class'
682
+ hooks = s ? '__trait_singleton_method_hooks' : '__trait_instance_method_hooks'
683
+
684
+ writer_code = <<-code
685
+ def #{ name }!
686
+ hooks = {}
687
+ hook_types = %w( pre munge cast type ducktype validate post toggle )
688
+
689
+ #{ klass }::__trait_search_path.each do |obj|
690
+
691
+ break if hooks.values_at(*hook_types).compact.size == hook_types.size
692
+
693
+ hook_types.each{|ht| hooks[ht] ||= obj.#{ hooks }['#{ name }'][ht]}
694
+
695
+ end
696
+
697
+ pre_hook, munge_hook, cast_hook, type_hook, ducktype_hook, validate_hook, post_hook, toggle_hook =
698
+ hooks.values_at(*hook_types)
699
+
700
+ toggle =
701
+ if defined? @________#{ name }_toggle
702
+ @________#{ name }_toggle
703
+ else
704
+ if toggle_hook
705
+ case toggle_hook
706
+ when Proc
707
+ t = Object.new
708
+ sc = class << t
709
+ self
710
+ end
711
+ sc.module_eval{
712
+ define_method 'shift' do
713
+ toggle_hook.call
714
+ end
715
+ define_method 'push' do
716
+ end
717
+ }
718
+ t
719
+ when Enumerable
720
+ toggle_hook.to_a
721
+ when NilClass, TrueClass
722
+ @________#{ name }_toggle = [true, false]
723
+ else
724
+ if toggle_hook.respond_to?('shift') and toggle_hook.respond_to?('push')
725
+ toggle_hook
726
+ else
727
+ raise "bad toggle <\#{ toggle_hook.inspect }>"
728
+ end
729
+ end
730
+ else
731
+ @________#{ name }_toggle = [true, false]
732
+ end
733
+ end
734
+
735
+ value = toggle.shift
736
+ toggle.push value
737
+
738
+ if pre_hook
739
+ [ pre_hook ].flatten.each do |pre|
740
+ if Proc === pre
741
+ case pre.arity
742
+ when 0
743
+ __trait_evaluate &pre
744
+ when 1
745
+ __trait_evaluate value, &pre
746
+ else
747
+ __trait_evaluate "#{ name }", value, &pre
748
+ end
749
+ else
750
+ case method("\#{ pre }").arity
751
+ when 0
752
+ send "\#{ pre }"
753
+ when 1
754
+ send "\#{ pre }", value
755
+ else
756
+ send "\#{ pre }", "#{ name }", value
757
+ end
758
+ end
759
+ end
760
+ end
761
+
762
+ if cast_hook
763
+ [ cast_hook ].flatten.each do |cast|
764
+ value =
765
+ if Proc === cast
766
+ __trait_evaluate value, &cast
767
+ else
768
+ send cast, value
769
+ end
770
+ end
771
+ end
772
+
773
+ if munge_hook
774
+ [ munge_hook ].flatten.each do |munge|
775
+ value =
776
+ if Proc === munge
777
+ __trait_evaluate value, &munge
778
+ else
779
+ value.send munge
780
+ end
781
+ end
782
+ end
783
+
784
+ if type_hook
785
+ [ type_hook ].flatten.each do |type|
786
+ is_valid =
787
+ if Proc === type
788
+ __trait_evaluate(value, &type) === value
789
+ else
790
+ type === value
791
+ end
792
+ raise ArgumentError,
793
+ "validation of <\#{ value.inspect }> failed!" unless is_valid
794
+ end
795
+ end
796
+
797
+ if ducktype_hook
798
+ [ ducktype_hook ].flatten.each do |ducktype|
799
+ is_valid =
800
+ if Proc === ducktype
801
+ value.respond_to? __trait_evaluate(value, &ducktype)
802
+ else
803
+ value.respond_to? ducktype
804
+ end
805
+ raise ArgumentError,
806
+ "validation of <\#{ value.inspect }> failed!" unless is_valid
807
+ end
808
+ end
809
+
810
+ if validate_hook
811
+ [ validate_hook ].flatten.each do |validate|
812
+ is_valid =
813
+ if Proc === validate
814
+ __trait_evaluate value, &validate
815
+ else
816
+ send "\#{ validate }", value
817
+ end
818
+ raise ArgumentError,
819
+ "validation of <\#{ value.inspect }> failed!" unless is_valid
820
+ end
821
+ end
822
+
823
+ @#{ name } = value
824
+
825
+ #@________#{ name }_set = true
826
+
827
+ if post_hook
828
+ [ post_hook ].flatten.each do |post|
829
+ if Proc === post
830
+ case post.arity
831
+ when 0
832
+ __trait_evaluate &post
833
+ when 1
834
+ __trait_evaluate value, &post
835
+ else
836
+ __trait_evaluate "#{ name }", value, &post
837
+ end
838
+ else
839
+ case method("\#{ post }").arity
840
+ when 0
841
+ send "\#{ post }"
842
+ when 1
843
+ send "\#{ post }", value
844
+ else
845
+ send "\#{ post }", "#{ name }", value
846
+ end
847
+ end
848
+ end
849
+ end
850
+
851
+ @#{ name }
852
+ end
853
+ #{ access_protection } '#{ name }='.intern
854
+ code
855
+ puts writer_code if Traits::DEBUG
856
+ writer_code
857
+ #--}}}
858
+ end
859
+
652
860
  def __trait_gen_access_protection_code name, access_protection = nil
653
861
  #--{{{
654
862
  access_protection ||= 'public'
@@ -661,7 +869,7 @@ class Module
661
869
  else
662
870
  "public '#{ name }'.intern"
663
871
  end
664
- puts access_protection_code if $__TRAIT_DEBUG__
872
+ puts access_protection_code if Traits::DEBUG
665
873
  access_protection_code
666
874
  #--}}}
667
875
  end
@@ -676,8 +884,8 @@ module TraitInit
676
884
  args, opts = argv.partition{|arg| not Hash === arg}
677
885
  args.flatten!
678
886
  opts = opts.inject({}){|h,h2| h.update h2}
679
- msgs = r_traits
680
- args.each{|arg| send msgs.shift, v}
887
+ msgs = self.class.r_traits
888
+ args.each{|arg| send msgs.shift, arg}
681
889
  opts.each do |k,v|
682
890
  k = "#{ k }"
683
891
  if respond_to? k
@@ -711,3 +919,44 @@ module TraitInit
711
919
  end
712
920
  #--}}}
713
921
  end
922
+
923
+ class OpenTraits
924
+ #--{{{
925
+ def initialize h = {}, &b
926
+ h.each{|k,v| trait k => v}
927
+ instance_eval &b if b
928
+ end
929
+
930
+ def method_missing m, *a, &b
931
+ m = m.to_s.delete '='
932
+ unless respond_to? m
933
+ if a.empty?
934
+ b ? trait(m, &b) : trait(m)
935
+ else
936
+ b ? trait(m => a.shift, &b) : trait(m => a.shift)
937
+ end
938
+ end
939
+ send m
940
+ end
941
+
942
+ def to_hash
943
+ rtraits.inject({}){|h,t| h.update t => send(t)}
944
+ end
945
+ alias_method "to_h", "to_hash"
946
+
947
+ def to_s *a, &b
948
+ to_hash.to_s *a, &b
949
+ end
950
+
951
+ def inspect *a, &b
952
+ to_hash.inspect *a, &b
953
+ end
954
+
955
+ def to_yaml *a, &b
956
+ to_hash.to_yaml *a, &b
957
+ end
958
+ #--}}}
959
+ end
960
+ def OpenTraits(*a, &b) OpenTraits::new(*a, &b) end
961
+ def opentraits(*a, &b) OpenTraits::new(*a, &b) end
962
+ def otraits(*a, &b) OpenTraits::new(*a, &b) end
@@ -18,6 +18,7 @@ class C
18
18
  p r?
19
19
  self.r = 42
20
20
  p r
21
+ p r?
21
22
  end
22
23
  end
23
24
  C::new.using_private_writer_and_query
@@ -31,6 +32,7 @@ class D
31
32
  p w?
32
33
  self.w = 'forty-two'
33
34
  p w
35
+ p w?
34
36
  end
35
37
  end
36
38
  D::new.using_private_reader
@@ -19,5 +19,5 @@ class C
19
19
  end
20
20
 
21
21
  c = C::new "li" => [4, 2], "ls" => %w[4 2]
22
- p c.li.join
23
- p c.ls.join
22
+ p c.li
23
+ p c.ls
@@ -0,0 +1,24 @@
1
+ require 'traits'
2
+ #
3
+ # the OpenTraits class is similar to an OpenStruct but, imho, easier to use.
4
+ # the otraits shorthand can be used to contruct one
5
+ #
6
+
7
+ #
8
+ # options passed as args dynamically create and init traits
9
+ #
10
+ config = otraits :port => 42
11
+ p config.port
12
+
13
+ #
14
+ # any passed block does the same but, via a method missing hood and traits
15
+ # getter/setters, the syntax is very clean
16
+ #
17
+ config = otraits{
18
+ port 42
19
+ host 'forty-two'
20
+ }
21
+ p config.port
22
+ p config.host
23
+ config.username 'zaphod'
24
+ p config
File without changes
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: traits
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.9.1
7
- date: 2006-06-01 00:00:00.000000 -06:00
6
+ version: 0.10.0
7
+ date: 2006-10-05 00:00:00.000000 -06:00
8
8
  summary: traits
9
9
  require_paths:
10
10
  - lib
@@ -36,8 +36,9 @@ files:
36
36
  - README.tmpl
37
37
  - gen_readme.rb
38
38
  - gemspec.rb
39
+ - traits-0.10.0.gem
39
40
  - lib/traits.rb
40
- - lib/traits-0.9.1.rb
41
+ - lib/traits-0.10.0.rb
41
42
  - sample/a.rb
42
43
  - sample/b.rb
43
44
  - sample/c.rb
@@ -53,6 +54,7 @@ files:
53
54
  - sample/m.rb
54
55
  - sample/n.rb
55
56
  - sample/p.rb
57
+ - sample/q.rb
56
58
  test_files: []
57
59
  rdoc_options: []
58
60
  extra_rdoc_files: []
OSZAR »