traits 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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: []