traits 0.9.0 → 0.9.1
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 +686 -0
- data/README.tmpl +136 -0
- data/gemspec.rb +23 -0
- data/gen_readme.rb +29 -0
- data/install.rb +201 -0
- data/lib/{traits-0.9.0.rb → traits-0.9.1.rb} +36 -10
- data/lib/traits.rb +36 -10
- data/sample/a.rb +22 -0
- data/sample/b.rb +14 -0
- data/sample/c.rb +49 -0
- data/sample/d.rb +30 -0
- data/sample/e.rb +10 -0
- data/sample/f.rb +25 -0
- data/sample/g.rb +16 -0
- data/sample/h.rb +17 -0
- data/sample/i.rb +36 -0
- data/sample/j.rb +23 -0
- data/sample/k.rb +36 -0
- data/sample/l.rb +15 -0
- data/sample/m.rb +24 -0
- data/sample/n.rb +37 -0
- data/sample/p.rb +23 -0
- metadata +25 -3
data/README.tmpl
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
URLS
|
2
|
+
|
3
|
+
http://rubyforge.org/projects/codeforpeople/
|
4
|
+
http://codeforpeople.com/lib/ruby/traits
|
5
|
+
|
6
|
+
ABOUT
|
7
|
+
|
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
|
10
|
+
priciples. traits.rb supports smart inheritence of class attributes and a
|
11
|
+
fistful of hooks for veryifying and munging attr values.
|
12
|
+
|
13
|
+
VERSION
|
14
|
+
|
15
|
+
0.9.1
|
16
|
+
|
17
|
+
HISTORY
|
18
|
+
|
19
|
+
0.9.0
|
20
|
+
- luke kaines made quite a few suggestions and bug reports that enabled this
|
21
|
+
release including making a few methods indifferent about string/symbol
|
22
|
+
args/keys and the introduction of a simple method 'trait_init' that can be
|
23
|
+
used to create keyword based initializers, eg:
|
24
|
+
|
25
|
+
require 'traits'
|
26
|
+
|
27
|
+
class C
|
28
|
+
include TraitInit
|
29
|
+
|
30
|
+
trait :a, :type => Integer
|
31
|
+
trait :b, :type => Integer
|
32
|
+
|
33
|
+
def initialize opts = {}
|
34
|
+
trait_init opts
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
C::new :a => 4, :b => 2
|
39
|
+
|
40
|
+
0.8.0
|
41
|
+
- traits now supports a whole slew of hooks that can be registered to fire
|
42
|
+
pre or post setting an attribute, to cast a value to another type, to
|
43
|
+
munge a value destructively, to require only certain types, to require a
|
44
|
+
certain ducktype signature, and to validate arguments passed. check out
|
45
|
+
sample/m.rb, sample/n.rb, or sample.o.rb to see it in action. the
|
46
|
+
mechanism is quite flexible allowing method names, lambdas of varying
|
47
|
+
arity, and lists of either/or to be passed to any hook.
|
48
|
+
|
49
|
+
- you can find a gem for trais on codeforpeople - but i've still not coded
|
50
|
+
up automated updating from codeforpeople to rubyforge so it won't show up
|
51
|
+
as a remote gem yet.
|
52
|
+
|
53
|
+
0.7.0
|
54
|
+
- patched in the support i had written eariler for 'hooks' to be called
|
55
|
+
pre/post setting a trait. plus shortcut to 'validate' traits which simply
|
56
|
+
sets up a 'pre' hook which is used as a predicate. eg:
|
57
|
+
|
58
|
+
class C; trait 'number', 'validate' => proc{|n| Numeric === n}
|
59
|
+
|
60
|
+
pre and post hooks are used in the same way, eg:
|
61
|
+
|
62
|
+
class C
|
63
|
+
trait 'a',
|
64
|
+
'pre' => proc{|val| p "#{ val } to set with"},
|
65
|
+
'post' => proc{|val| p "#{ val } set"},
|
66
|
+
end
|
67
|
+
|
68
|
+
but the really cool thing is that all of these blocks are both passed the
|
69
|
+
value in question but also evaluate with 'self' set appropriately. eg
|
70
|
+
|
71
|
+
class Car
|
72
|
+
positive_int = lambda{|n| Fixnum === n and n > 0}
|
73
|
+
legal = proc{|s| s < speed_limit}
|
74
|
+
|
75
|
+
trait 'speed_limit', 'validate' => positive_int, 'default' => 42
|
76
|
+
trait 'speed', 'validate' => legal
|
77
|
+
end
|
78
|
+
|
79
|
+
c = Car::new
|
80
|
+
c.speed = 115
|
81
|
+
|
82
|
+
works as you'd expect:
|
83
|
+
|
84
|
+
(eval):14:in `speed=': validation of speed=(115) failed! (ArgumentError)
|
85
|
+
from a.rb:13
|
86
|
+
|
87
|
+
0.6.0
|
88
|
+
- fixed bug in where a default trait given as an empty array, eg:
|
89
|
+
|
90
|
+
class C; has 'a' => []; end
|
91
|
+
|
92
|
+
was exploded into the empty list when passed to the setter to initialize
|
93
|
+
the default value.
|
94
|
+
|
95
|
+
0.5.0
|
96
|
+
- general code cleanup
|
97
|
+
|
98
|
+
0.4.0
|
99
|
+
- tweaked writer code so multiple values can be passed to setters
|
100
|
+
- tweaked method of running blocks to use instance_eval so explicit 'this'
|
101
|
+
arg is no longer needed (though it can still be used)
|
102
|
+
|
103
|
+
0.3.0
|
104
|
+
added ability of default values to be specified with block for deferred
|
105
|
+
context sensitive initialization (see sample/c.rb)
|
106
|
+
|
107
|
+
0.1.0
|
108
|
+
|
109
|
+
completely reworked impl so NO parsing of inspect strings is required -
|
110
|
+
it's all straight methods (albeit quite confusing ones) now. the
|
111
|
+
interface is unchanged.
|
112
|
+
|
113
|
+
0.0.0
|
114
|
+
|
115
|
+
initial version
|
116
|
+
|
117
|
+
|
118
|
+
AUTHOR
|
119
|
+
|
120
|
+
ara [dot] t [dot] howard [at] noaa [dot] gov
|
121
|
+
|
122
|
+
SAMPLES
|
123
|
+
|
124
|
+
@samples
|
125
|
+
|
126
|
+
CAVEATS
|
127
|
+
|
128
|
+
this library is experimental and subject to change - though it has not for
|
129
|
+
several versions and much of my code hinges is on it now so you can expect the
|
130
|
+
interface to be stable in the near future - the only changes planned are those
|
131
|
+
that fix bugs or add features.
|
132
|
+
|
133
|
+
LICENSE
|
134
|
+
|
135
|
+
same as ruby's
|
136
|
+
|
data/gemspec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
Gem::Specification::new do |spec|
|
6
|
+
spec.name = lib
|
7
|
+
spec.version = version
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.summary = lib
|
10
|
+
|
11
|
+
spec.files = Dir::glob "**/**"
|
12
|
+
spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
|
13
|
+
|
14
|
+
spec.require_path = "lib"
|
15
|
+
spec.autorequire = lib
|
16
|
+
|
17
|
+
spec.has_rdoc = File::exist? "doc"
|
18
|
+
spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
|
19
|
+
|
20
|
+
spec.author = "Ara T. Howard"
|
21
|
+
spec.email = "ara.t.howard@noaa.gov"
|
22
|
+
spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
|
23
|
+
end
|
data/gen_readme.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
$VERBOSE=nil
|
2
|
+
def indent s, n = 2
|
3
|
+
ws = ' ' * n
|
4
|
+
s.gsub %r/^/, ws
|
5
|
+
end
|
6
|
+
|
7
|
+
template = IO::read 'README.tmpl'
|
8
|
+
|
9
|
+
samples = ''
|
10
|
+
prompt = '~ > '
|
11
|
+
|
12
|
+
Dir['sample/*'].each do |sample|
|
13
|
+
samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
|
14
|
+
|
15
|
+
cmd = "cat #{ sample }"
|
16
|
+
samples << indent(prompt + cmd, 2) << "\n\n"
|
17
|
+
samples << indent(`#{ cmd }`, 4) << "\n"
|
18
|
+
|
19
|
+
cmd = "ruby #{ sample }"
|
20
|
+
samples << indent(prompt + cmd, 2) << "\n\n"
|
21
|
+
|
22
|
+
cmd = "ruby -I ./lib -r ./lib/traits.rb #{ sample }"
|
23
|
+
samples << indent(`#{ cmd } 2>&1`, 4) << "\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
#samples.gsub! %r/^/, ' '
|
27
|
+
|
28
|
+
readme = template.gsub %r/^\s*@samples\s*$/, samples
|
29
|
+
print readme
|
data/install.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'find'
|
4
|
+
require 'ftools'
|
5
|
+
require 'tempfile'
|
6
|
+
include Config
|
7
|
+
|
8
|
+
LIBDIR = "lib"
|
9
|
+
LIBDIR_MODE = 0644
|
10
|
+
|
11
|
+
BINDIR = "bin"
|
12
|
+
BINDIR_MODE = 0755
|
13
|
+
|
14
|
+
|
15
|
+
$srcdir = CONFIG["srcdir"]
|
16
|
+
$version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
17
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", $version)
|
18
|
+
$archdir = File.join($libdir, CONFIG["arch"])
|
19
|
+
$site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
20
|
+
$bindir = CONFIG["bindir"] || CONFIG['BINDIR']
|
21
|
+
$ruby_install_name = CONFIG['ruby_install_name'] || CONFIG['RUBY_INSTALL_NAME'] || 'ruby'
|
22
|
+
$ruby_ext = CONFIG['EXEEXT'] || ''
|
23
|
+
$ruby = File.join($bindir, ($ruby_install_name + $ruby_ext))
|
24
|
+
|
25
|
+
if !$site_libdir
|
26
|
+
$site_libdir = File.join($libdir, "site_ruby")
|
27
|
+
elsif $site_libdir !~ %r/#{Regexp.quote($version)}/
|
28
|
+
$site_libdir = File.join($site_libdir, $version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def install_rb(srcdir=nil, destdir=nil, mode=nil, bin=nil)
|
32
|
+
#{{{
|
33
|
+
path = []
|
34
|
+
dir = []
|
35
|
+
Find.find(srcdir) do |f|
|
36
|
+
next unless FileTest.file?(f)
|
37
|
+
next if (f = f[srcdir.length+1..-1]) == nil
|
38
|
+
next if (/CVS$/ =~ File.dirname(f))
|
39
|
+
next if f =~ %r/\.lnk/
|
40
|
+
path.push f
|
41
|
+
dir |= [File.dirname(f)]
|
42
|
+
end
|
43
|
+
for f in dir
|
44
|
+
next if f == "."
|
45
|
+
next if f == "CVS"
|
46
|
+
File::makedirs(File.join(destdir, f))
|
47
|
+
end
|
48
|
+
for f in path
|
49
|
+
next if (/\~$/ =~ f)
|
50
|
+
next if (/^\./ =~ File.basename(f))
|
51
|
+
unless bin
|
52
|
+
File::install(File.join(srcdir, f), File.join(destdir, f), mode, true)
|
53
|
+
else
|
54
|
+
from = File.join(srcdir, f)
|
55
|
+
to = File.join(destdir, f)
|
56
|
+
shebangify(from) do |sf|
|
57
|
+
$deferr.print from, " -> ", File::catname(from, to), "\n"
|
58
|
+
$deferr.printf "chmod %04o %s\n", mode, to
|
59
|
+
File::install(sf, to, mode, false)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
#}}}
|
64
|
+
end
|
65
|
+
def shebangify f
|
66
|
+
#{{{
|
67
|
+
open(f) do |fd|
|
68
|
+
buf = fd.read 42
|
69
|
+
if buf =~ %r/^\s*#\s*!.*ruby/o
|
70
|
+
ftmp = Tempfile::new("#{ $$ }_#{ File::basename(f) }")
|
71
|
+
begin
|
72
|
+
fd.rewind
|
73
|
+
ftmp.puts "#!#{ $ruby }"
|
74
|
+
while((buf = fd.read(8192)))
|
75
|
+
ftmp.write buf
|
76
|
+
end
|
77
|
+
ftmp.close
|
78
|
+
yield ftmp.path
|
79
|
+
ensure
|
80
|
+
ftmp.close!
|
81
|
+
end
|
82
|
+
else
|
83
|
+
yield f
|
84
|
+
end
|
85
|
+
end
|
86
|
+
#}}}
|
87
|
+
end
|
88
|
+
def ARGV.switch
|
89
|
+
#{{{
|
90
|
+
return nil if self.empty?
|
91
|
+
arg = self.shift
|
92
|
+
return nil if arg == '--'
|
93
|
+
if arg =~ /^-(.)(.*)/
|
94
|
+
return arg if $1 == '-'
|
95
|
+
raise 'unknown switch "-"' if $2.index('-')
|
96
|
+
self.unshift "-#{$2}" if $2.size > 0
|
97
|
+
"-#{$1}"
|
98
|
+
else
|
99
|
+
self.unshift arg
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
#}}}
|
103
|
+
end
|
104
|
+
def ARGV.req_arg
|
105
|
+
#{{{
|
106
|
+
self.shift || raise('missing argument')
|
107
|
+
#}}}
|
108
|
+
end
|
109
|
+
def linkify d, linked = []
|
110
|
+
#--{{{
|
111
|
+
if test ?d, d
|
112
|
+
versioned = Dir[ File::join(d, "*-[0-9].[0-9].[0-9].rb") ]
|
113
|
+
versioned.each do |v|
|
114
|
+
src, dst = v, v.gsub(%r/\-[\d\.]+\.rb$/, '.rb')
|
115
|
+
lnk = nil
|
116
|
+
begin
|
117
|
+
if test ?l, dst
|
118
|
+
lnk = "#{ dst }.lnk"
|
119
|
+
puts "#{ dst } -> #{ lnk }"
|
120
|
+
File::rename dst, lnk
|
121
|
+
end
|
122
|
+
unless test ?e, dst
|
123
|
+
puts "#{ src } -> #{ dst }"
|
124
|
+
File::copy src, dst
|
125
|
+
linked << dst
|
126
|
+
end
|
127
|
+
ensure
|
128
|
+
if lnk
|
129
|
+
at_exit do
|
130
|
+
puts "#{ lnk } -> #{ dst }"
|
131
|
+
File::rename lnk, dst
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
linked
|
138
|
+
#--}}}
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
#
|
143
|
+
# main program
|
144
|
+
#
|
145
|
+
|
146
|
+
libdir = $site_libdir
|
147
|
+
bindir = $bindir
|
148
|
+
no_linkify = false
|
149
|
+
linked = nil
|
150
|
+
help = false
|
151
|
+
|
152
|
+
usage = <<-usage
|
153
|
+
#{ File::basename $0 }
|
154
|
+
-d, --destdir <destdir>
|
155
|
+
-l, --libdir <libdir>
|
156
|
+
-b, --bindir <bindir>
|
157
|
+
-r, --ruby <ruby>
|
158
|
+
-n, --no_linkify
|
159
|
+
-h, --help
|
160
|
+
usage
|
161
|
+
|
162
|
+
begin
|
163
|
+
while switch = ARGV.switch
|
164
|
+
case switch
|
165
|
+
when '-d', '--destdir'
|
166
|
+
libdir = ARGV.req_arg
|
167
|
+
when '-l', '--libdir'
|
168
|
+
libdir = ARGV.req_arg
|
169
|
+
when '-b', '--bindir'
|
170
|
+
bindir = ARGV.req_arg
|
171
|
+
when '-r', '--ruby'
|
172
|
+
$ruby = ARGV.req_arg
|
173
|
+
when '-n', '--no_linkify'
|
174
|
+
no_linkify = true
|
175
|
+
when '-h', '--help'
|
176
|
+
help = true
|
177
|
+
else
|
178
|
+
raise "unknown switch #{switch.dump}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
rescue
|
182
|
+
STDERR.puts $!.to_s
|
183
|
+
STDERR.puts usage
|
184
|
+
exit 1
|
185
|
+
end
|
186
|
+
|
187
|
+
if help
|
188
|
+
STDOUT.puts usage
|
189
|
+
exit
|
190
|
+
end
|
191
|
+
|
192
|
+
unless no_linkify
|
193
|
+
linked = linkify('lib') + linkify('bin')
|
194
|
+
end
|
195
|
+
|
196
|
+
install_rb(LIBDIR, libdir, LIBDIR_MODE)
|
197
|
+
install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
|
198
|
+
|
199
|
+
if linked
|
200
|
+
linked.each{|path| File::rm_f path}
|
201
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
$__TRAIT_DEBUG__ = ENV['__TRAIT_DEBUG__'] || ENV['TRAIT_DEBUG'] || ENV['DEBUG']
|
2
|
-
$__TRAIT_VERSION__ = "0.9.
|
2
|
+
$__TRAIT_VERSION__ = "0.9.1"
|
3
3
|
|
4
4
|
class Object
|
5
5
|
#--{{{
|
@@ -670,18 +670,44 @@ end
|
|
670
670
|
|
671
671
|
module TraitInit
|
672
672
|
#--{{{
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
673
|
+
module InstaceMethods
|
674
|
+
def trait_init *argv
|
675
|
+
#--{{{
|
676
|
+
args, opts = argv.partition{|arg| not Hash === arg}
|
677
|
+
args.flatten!
|
678
|
+
opts = opts.inject({}){|h,h2| h.update h2}
|
679
|
+
msgs = r_traits
|
680
|
+
args.each{|arg| send msgs.shift, v}
|
681
|
+
opts.each do |k,v|
|
682
|
+
k = "#{ k }"
|
683
|
+
if respond_to? k
|
684
|
+
send k, v
|
685
|
+
else
|
686
|
+
raise ArgumentError, "invalid trait -- #{ self.class }##{ k }"
|
687
|
+
end
|
681
688
|
end
|
689
|
+
#--}}}
|
682
690
|
end
|
691
|
+
alias_method "traitinit", "trait_init"
|
692
|
+
end
|
693
|
+
module ClassMethods
|
694
|
+
def trait_initialize *a, &b
|
695
|
+
traits *a unless a.empty?
|
696
|
+
module_eval{
|
697
|
+
def initialize(*a, &b)
|
698
|
+
super() if defined? super
|
699
|
+
trait_init *a
|
700
|
+
end
|
701
|
+
}
|
702
|
+
end
|
703
|
+
alias_method "traitinitialize", "trait_initialize"
|
704
|
+
end
|
705
|
+
def self.included other
|
706
|
+
#--{{{
|
707
|
+
other.extend ClassMethods
|
708
|
+
other.module_eval{ include InstaceMethods }
|
709
|
+
super
|
683
710
|
#--}}}
|
684
711
|
end
|
685
|
-
alias_method "traitinit", "trait_init"
|
686
712
|
#--}}}
|
687
713
|
end
|