traits 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
@@ -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
@@ -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
@@ -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.0"
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
- def trait_init opts = {}
674
- #--{{{
675
- opts.each do |k,v|
676
- k = "#{ k }"
677
- if respond_to? k
678
- send k, v
679
- else
680
- raise ArgumentError, "invalid trait -- #{ self.class }##{ k }"
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