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 +76 -7
- data/README.tmpl +31 -3
- data/gen_readme.rb +1 -1
- data/lib/{traits-0.9.1.rb → traits-0.10.0.rb} +261 -12
- data/lib/traits.rb +261 -12
- data/sample/i.rb +2 -0
- data/sample/p.rb +2 -2
- data/sample/q.rb +24 -0
- data/traits-0.10.0.gem +0 -0
- metadata +5 -3
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.
|
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.
|
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
|
668
|
-
p c.ls
|
699
|
+
p c.li
|
700
|
+
p c.ls
|
669
701
|
|
670
702
|
~ > ruby sample/p.rb
|
671
703
|
|
672
|
-
|
673
|
-
"
|
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
|
data/README.tmpl
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.
|
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.
|
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
|
data/gen_readme.rb
CHANGED
@@ -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
|
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
|
-
|
2
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
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
|
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,
|
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
|
data/lib/traits.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
|
2
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
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
|
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,
|
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
|
data/sample/i.rb
CHANGED
data/sample/p.rb
CHANGED
data/sample/q.rb
ADDED
@@ -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
|
data/traits-0.10.0.gem
ADDED
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.
|
7
|
-
date: 2006-
|
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.
|
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: []
|