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 +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: []
|