fattr 1.0.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 +252 -0
- data/README.tmpl +57 -0
- data/gemspec.rb +28 -0
- data/gen_readme.rb +32 -0
- data/install.rb +214 -0
- data/lib/fattr.rb +125 -0
- data/samples/a.rb +21 -0
- data/samples/b.rb +22 -0
- data/samples/c.rb +12 -0
- data/samples/d.rb +34 -0
- data/samples/e.rb +18 -0
- data/samples/f.rb +21 -0
- metadata +66 -0
data/README
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
NAME
|
2
|
+
fattr.rb
|
3
|
+
|
4
|
+
INSTALL
|
5
|
+
gem install fattrs
|
6
|
+
|
7
|
+
URIS
|
8
|
+
http://codeforpeople.com/lib/ruby
|
9
|
+
http://rubyforge.org/projects/codeforpeople/
|
10
|
+
http://codeforpeople.rubyforge.org/svn/
|
11
|
+
|
12
|
+
SYNOPSIS
|
13
|
+
fattr.rb is a "fatter attr" for ruby. fattr.rb supercedes attributes.rb as
|
14
|
+
that library, even though it added only one method to the global namespace,
|
15
|
+
collided too frequently with user code - in particular rails' code.
|
16
|
+
|
17
|
+
the implementation of fattr.rb borrows many of the best ideas from the
|
18
|
+
metakoans.rb ruby quiz
|
19
|
+
|
20
|
+
http://www.rubyquiz.com/quiz67.html
|
21
|
+
|
22
|
+
in particular the solutions of Christian Neukirchen and Florian Gross along
|
23
|
+
with concepts from the original traits.rb lib
|
24
|
+
|
25
|
+
key features provided by fattrs are
|
26
|
+
|
27
|
+
- ability to specify default values for attrs and definition time. values
|
28
|
+
can be literal objects or blocks, which are evaluated in the context of
|
29
|
+
self to initialize the variable
|
30
|
+
|
31
|
+
- classes remember which fattrs they've defined and this information is
|
32
|
+
available to client code
|
33
|
+
|
34
|
+
- a whole suite of methods is defined by calls to #fattrs including
|
35
|
+
getter, setter, query (var?) and banger (var! - which forces
|
36
|
+
re-initialization from the default value/block)
|
37
|
+
|
38
|
+
- ability to define multiple fattrs at once using key => value pairs
|
39
|
+
|
40
|
+
- fast lookup of whether or not a class has defined a certain fattr
|
41
|
+
|
42
|
+
- fattrs can be defined on objects on a per singleton basis
|
43
|
+
|
44
|
+
- getters acts as setters if an argument is given to them
|
45
|
+
|
46
|
+
- block caching, calling an fattr with a block sets the instance
|
47
|
+
variable to that block
|
48
|
+
|
49
|
+
all this in < 100 lines of code
|
50
|
+
|
51
|
+
HISTORY
|
52
|
+
5.0.0:
|
53
|
+
port from attributes.rb retaining all the same features of that version of
|
54
|
+
attributes.rb
|
55
|
+
|
56
|
+
SAMPLES
|
57
|
+
|
58
|
+
<========< samples/a.rb >========>
|
59
|
+
|
60
|
+
~ > cat samples/a.rb
|
61
|
+
|
62
|
+
#
|
63
|
+
# basic usage is like attr, but note that fattr defines a suite of methods
|
64
|
+
#
|
65
|
+
require 'fattr'
|
66
|
+
|
67
|
+
class C
|
68
|
+
fattr 'a'
|
69
|
+
end
|
70
|
+
|
71
|
+
c = C.new
|
72
|
+
|
73
|
+
c.a = 42
|
74
|
+
p c.a #=> 42
|
75
|
+
p 'forty-two' if c.a? #=> 'forty-two'
|
76
|
+
|
77
|
+
#
|
78
|
+
# fattrs works on object too
|
79
|
+
#
|
80
|
+
o = Object.new
|
81
|
+
o.fattr 'answer' => 42
|
82
|
+
p o.answer #=> 42
|
83
|
+
|
84
|
+
~ > ruby samples/a.rb
|
85
|
+
|
86
|
+
42
|
87
|
+
"forty-two"
|
88
|
+
42
|
89
|
+
|
90
|
+
|
91
|
+
<========< samples/b.rb >========>
|
92
|
+
|
93
|
+
~ > cat samples/b.rb
|
94
|
+
|
95
|
+
#
|
96
|
+
# default values may be given either directly or as a block which will be
|
97
|
+
# evaluated in the context of self. in both cases (value or block) the
|
98
|
+
# default is set only once and only if needed - it's a lazy evaluation. the
|
99
|
+
# 'banger' method can be used to re-initialize a variable at any point whether
|
100
|
+
# or not it's already been initialized.
|
101
|
+
#
|
102
|
+
require 'fattr'
|
103
|
+
|
104
|
+
class C
|
105
|
+
fattr :a => 42
|
106
|
+
fattr(:b){ Float a }
|
107
|
+
end
|
108
|
+
|
109
|
+
c = C.new
|
110
|
+
p c.a #=> 42
|
111
|
+
p c.b #=> 42.0
|
112
|
+
|
113
|
+
c.a = 43
|
114
|
+
p c.a #=> 43
|
115
|
+
c.a!
|
116
|
+
p c.a #=> 42
|
117
|
+
|
118
|
+
~ > ruby samples/b.rb
|
119
|
+
|
120
|
+
42
|
121
|
+
42.0
|
122
|
+
43
|
123
|
+
42
|
124
|
+
|
125
|
+
|
126
|
+
<========< samples/c.rb >========>
|
127
|
+
|
128
|
+
~ > cat samples/c.rb
|
129
|
+
|
130
|
+
#
|
131
|
+
# multiple values may by given, plain names and key/val pairs may be mixed.
|
132
|
+
#
|
133
|
+
require 'fattr'
|
134
|
+
|
135
|
+
class C
|
136
|
+
fattrs 'x', 'y' => 0b101000, 'z' => 0b10
|
137
|
+
end
|
138
|
+
|
139
|
+
c = C.new
|
140
|
+
c.x = c.y + c.z
|
141
|
+
p c.x #=> 42
|
142
|
+
|
143
|
+
~ > ruby samples/c.rb
|
144
|
+
|
145
|
+
42
|
146
|
+
|
147
|
+
|
148
|
+
<========< samples/d.rb >========>
|
149
|
+
|
150
|
+
~ > cat samples/d.rb
|
151
|
+
|
152
|
+
#
|
153
|
+
# a nice feature is that all fattrs are enumerated in the class. this,
|
154
|
+
# combined with the fact that the getter method is defined so as to delegate
|
155
|
+
# to the setter when an argument is given, means bulk initialization and/or
|
156
|
+
# fattr traversal is very easy.
|
157
|
+
#
|
158
|
+
require 'fattr'
|
159
|
+
|
160
|
+
class C
|
161
|
+
fattrs %w( x y z )
|
162
|
+
|
163
|
+
def fattrs
|
164
|
+
self.class.fattrs
|
165
|
+
end
|
166
|
+
|
167
|
+
def initialize
|
168
|
+
fattrs.each_with_index{|a,i| send a, i}
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_hash
|
172
|
+
fattrs.inject({}){|h,a| h.update a => send(a)}
|
173
|
+
end
|
174
|
+
|
175
|
+
def inspect
|
176
|
+
to_hash.inspect
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
c = C.new
|
181
|
+
p c.fattrs
|
182
|
+
p c
|
183
|
+
|
184
|
+
c.x 'forty-two'
|
185
|
+
p c.x
|
186
|
+
|
187
|
+
~ > ruby samples/d.rb
|
188
|
+
|
189
|
+
["x", "y", "z"]
|
190
|
+
{"x"=>0, "y"=>1, "z"=>2}
|
191
|
+
"forty-two"
|
192
|
+
|
193
|
+
|
194
|
+
<========< samples/e.rb >========>
|
195
|
+
|
196
|
+
~ > cat samples/e.rb
|
197
|
+
|
198
|
+
#
|
199
|
+
# my favourite element of fattrs is that getters can also be setters.
|
200
|
+
# this allows incredibly clean looking code like
|
201
|
+
#
|
202
|
+
require 'fattr'
|
203
|
+
|
204
|
+
class Config
|
205
|
+
fattrs %w( host port)
|
206
|
+
def initialize(&block) instance_eval &block end
|
207
|
+
end
|
208
|
+
|
209
|
+
conf = Config.new{
|
210
|
+
host 'codeforpeople.org'
|
211
|
+
|
212
|
+
port 80
|
213
|
+
}
|
214
|
+
|
215
|
+
p conf
|
216
|
+
|
217
|
+
~ > ruby samples/e.rb
|
218
|
+
|
219
|
+
#<Config:0x22ab0 @port=80, @host="codeforpeople.org">
|
220
|
+
|
221
|
+
|
222
|
+
<========< samples/f.rb >========>
|
223
|
+
|
224
|
+
~ > cat samples/f.rb
|
225
|
+
|
226
|
+
#
|
227
|
+
# of course fattrs works as well at class/module level as at instance
|
228
|
+
# level
|
229
|
+
#
|
230
|
+
require 'fattr'
|
231
|
+
|
232
|
+
module Logging
|
233
|
+
Level_names = {
|
234
|
+
0 => 'INFO',
|
235
|
+
# ...
|
236
|
+
42 => 'DEBUG',
|
237
|
+
}
|
238
|
+
|
239
|
+
class << self
|
240
|
+
fattr 'level' => 42
|
241
|
+
fattr('level_name'){ Level_names[level] }
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
p Logging.level
|
246
|
+
p Logging.level_name
|
247
|
+
|
248
|
+
~ > ruby samples/f.rb
|
249
|
+
|
250
|
+
42
|
251
|
+
"DEBUG"
|
252
|
+
|
data/README.tmpl
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
NAME
|
2
|
+
fattr.rb
|
3
|
+
|
4
|
+
INSTALL
|
5
|
+
gem install fattrs
|
6
|
+
|
7
|
+
URIS
|
8
|
+
http://codeforpeople.com/lib/ruby
|
9
|
+
http://rubyforge.org/projects/codeforpeople/
|
10
|
+
http://codeforpeople.rubyforge.org/svn/
|
11
|
+
|
12
|
+
SYNOPSIS
|
13
|
+
fattr.rb is a "fatter attr" for ruby. fattr.rb supercedes attributes.rb as
|
14
|
+
that library, even though it added only one method to the global namespace,
|
15
|
+
collided too frequently with user code - in particular rails' code.
|
16
|
+
|
17
|
+
the implementation of fattr.rb borrows many of the best ideas from the
|
18
|
+
metakoans.rb ruby quiz
|
19
|
+
|
20
|
+
http://www.rubyquiz.com/quiz67.html
|
21
|
+
|
22
|
+
in particular the solutions of Christian Neukirchen and Florian Gross along
|
23
|
+
with concepts from the original traits.rb lib
|
24
|
+
|
25
|
+
key features provided by fattrs are
|
26
|
+
|
27
|
+
- ability to specify default values for attrs and definition time. values
|
28
|
+
can be literal objects or blocks, which are evaluated in the context of
|
29
|
+
self to initialize the variable
|
30
|
+
|
31
|
+
- classes remember which fattrs they've defined and this information is
|
32
|
+
available to client code
|
33
|
+
|
34
|
+
- a whole suite of methods is defined by calls to #fattrs including
|
35
|
+
getter, setter, query (var?) and banger (var! - which forces
|
36
|
+
re-initialization from the default value/block)
|
37
|
+
|
38
|
+
- ability to define multiple fattrs at once using key => value pairs
|
39
|
+
|
40
|
+
- fast lookup of whether or not a class has defined a certain fattr
|
41
|
+
|
42
|
+
- fattrs can be defined on objects on a per singleton basis
|
43
|
+
|
44
|
+
- getters acts as setters if an argument is given to them
|
45
|
+
|
46
|
+
- block caching, calling an fattr with a block sets the instance
|
47
|
+
variable to that block
|
48
|
+
|
49
|
+
all this in < 100 lines of code
|
50
|
+
|
51
|
+
HISTORY
|
52
|
+
5.0.0:
|
53
|
+
port from attributes.rb retaining all the same features of that version of
|
54
|
+
attributes.rb
|
55
|
+
|
56
|
+
SAMPLES
|
57
|
+
@samples
|
data/gemspec.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
Gem::Specification::new do |spec|
|
7
|
+
$VERBOSE = nil
|
8
|
+
spec.name = lib
|
9
|
+
spec.version = version
|
10
|
+
spec.platform = Gem::Platform::RUBY
|
11
|
+
spec.summary = lib
|
12
|
+
|
13
|
+
spec.files = Dir::glob "**/**"
|
14
|
+
spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
|
15
|
+
|
16
|
+
spec.require_path = "lib"
|
17
|
+
spec.autorequire = lib
|
18
|
+
|
19
|
+
spec.has_rdoc = File::exist? "doc"
|
20
|
+
spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
|
21
|
+
#spec.add_dependency 'pervasives', '>= 1.0'
|
22
|
+
|
23
|
+
spec.extensions << "extconf.rb" if File::exists? "extconf.rb"
|
24
|
+
|
25
|
+
spec.author = "Ara T. Howard"
|
26
|
+
spec.email = "ara.t.howard@gmail.com"
|
27
|
+
spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
|
28
|
+
end
|
data/gen_readme.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
$VERBOSE=nil
|
4
|
+
|
5
|
+
def indent s, n = 2
|
6
|
+
ws = ' ' * n
|
7
|
+
s.gsub %r/^/, ws
|
8
|
+
end
|
9
|
+
|
10
|
+
template = IO::read 'README.tmpl'
|
11
|
+
|
12
|
+
samples = ''
|
13
|
+
prompt = '~ > '
|
14
|
+
|
15
|
+
Dir['sample*/*'] .each do |sample|
|
16
|
+
samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
|
17
|
+
|
18
|
+
cmd = "cat #{ sample }"
|
19
|
+
samples << indent(prompt + cmd, 2) << "\n\n"
|
20
|
+
samples << indent(`#{ cmd }`, 4) << "\n"
|
21
|
+
|
22
|
+
cmd = "ruby #{ sample }"
|
23
|
+
samples << indent(prompt + cmd, 2) << "\n\n"
|
24
|
+
|
25
|
+
cmd = "ruby -Ilib #{ sample }"
|
26
|
+
samples << indent(`#{ cmd } 2>&1`, 4) << "\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
#samples.gsub! %r/^/, ' '
|
30
|
+
|
31
|
+
readme = template.gsub %r/^\s*@samples\s*$/, samples
|
32
|
+
print readme
|
data/install.rb
ADDED
@@ -0,0 +1,214 @@
|
|
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 (/\.svn/ =~ File.dirname(f))
|
40
|
+
next if f =~ %r/\.lnk/
|
41
|
+
next if f =~ %r/\.svn/
|
42
|
+
next if f =~ %r/\.swp/
|
43
|
+
next if f =~ %r/\.svn/
|
44
|
+
path.push f
|
45
|
+
dir |= [File.dirname(f)]
|
46
|
+
end
|
47
|
+
for f in dir
|
48
|
+
next if f == "."
|
49
|
+
next if f == "CVS"
|
50
|
+
File::makedirs(File.join(destdir, f))
|
51
|
+
end
|
52
|
+
for f in path
|
53
|
+
next if (/\~$/ =~ f)
|
54
|
+
next if (/^\./ =~ File.basename(f))
|
55
|
+
unless bin
|
56
|
+
File::install(File.join(srcdir, f), File.join(destdir, f), mode, true)
|
57
|
+
else
|
58
|
+
from = File.join(srcdir, f)
|
59
|
+
to = File.join(destdir, f)
|
60
|
+
shebangify(from) do |sf|
|
61
|
+
$deferr.print from, " -> ", File::catname(from, to), "\n"
|
62
|
+
$deferr.printf "chmod %04o %s\n", mode, to
|
63
|
+
File::install(sf, to, mode, false)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
#}}}
|
68
|
+
end
|
69
|
+
def shebangify f
|
70
|
+
#{{{
|
71
|
+
open(f) do |fd|
|
72
|
+
buf = fd.read 42
|
73
|
+
if buf =~ %r/^\s*#\s*!.*ruby/o
|
74
|
+
ftmp = Tempfile::new("#{ $$ }_#{ File::basename(f) }")
|
75
|
+
begin
|
76
|
+
fd.rewind
|
77
|
+
ftmp.puts "#!#{ $ruby }"
|
78
|
+
while((buf = fd.read(8192)))
|
79
|
+
ftmp.write buf
|
80
|
+
end
|
81
|
+
ftmp.close
|
82
|
+
yield ftmp.path
|
83
|
+
ensure
|
84
|
+
ftmp.close!
|
85
|
+
end
|
86
|
+
else
|
87
|
+
yield f
|
88
|
+
end
|
89
|
+
end
|
90
|
+
#}}}
|
91
|
+
end
|
92
|
+
def ARGV.switch
|
93
|
+
#{{{
|
94
|
+
return nil if self.empty?
|
95
|
+
arg = self.shift
|
96
|
+
return nil if arg == '--'
|
97
|
+
if arg =~ /^-(.)(.*)/
|
98
|
+
return arg if $1 == '-'
|
99
|
+
raise 'unknown switch "-"' if $2.index('-')
|
100
|
+
self.unshift "-#{$2}" if $2.size > 0
|
101
|
+
"-#{$1}"
|
102
|
+
else
|
103
|
+
self.unshift arg
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
#}}}
|
107
|
+
end
|
108
|
+
def ARGV.req_arg
|
109
|
+
#{{{
|
110
|
+
self.shift || raise('missing argument')
|
111
|
+
#}}}
|
112
|
+
end
|
113
|
+
def linkify d, linked = []
|
114
|
+
#--{{{
|
115
|
+
if test ?d, d
|
116
|
+
versioned = Dir[ File::join(d, "*-[0-9].[0-9].[0-9].rb") ]
|
117
|
+
versioned.each do |v|
|
118
|
+
src, dst = v, v.gsub(%r/\-[\d\.]+\.rb$/, '.rb')
|
119
|
+
lnk = nil
|
120
|
+
begin
|
121
|
+
if test ?l, dst
|
122
|
+
lnk = "#{ dst }.lnk"
|
123
|
+
puts "#{ dst } -> #{ lnk }"
|
124
|
+
File::rename dst, lnk
|
125
|
+
end
|
126
|
+
unless test ?e, dst
|
127
|
+
puts "#{ src } -> #{ dst }"
|
128
|
+
File::copy src, dst
|
129
|
+
linked << dst
|
130
|
+
end
|
131
|
+
ensure
|
132
|
+
if lnk
|
133
|
+
at_exit do
|
134
|
+
puts "#{ lnk } -> #{ dst }"
|
135
|
+
File::rename lnk, dst
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
linked
|
142
|
+
#--}}}
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
#
|
147
|
+
# main program
|
148
|
+
#
|
149
|
+
|
150
|
+
libdir = $site_libdir
|
151
|
+
bindir = $bindir
|
152
|
+
no_linkify = false
|
153
|
+
linked = nil
|
154
|
+
help = false
|
155
|
+
|
156
|
+
usage = <<-usage
|
157
|
+
#{ File::basename $0 }
|
158
|
+
-d, --destdir <destdir>
|
159
|
+
-l, --libdir <libdir>
|
160
|
+
-b, --bindir <bindir>
|
161
|
+
-r, --ruby <ruby>
|
162
|
+
-n, --no_linkify
|
163
|
+
-s, --sudo
|
164
|
+
-h, --help
|
165
|
+
usage
|
166
|
+
|
167
|
+
begin
|
168
|
+
while switch = ARGV.switch
|
169
|
+
case switch
|
170
|
+
when '-d', '--destdir'
|
171
|
+
libdir = ARGV.req_arg
|
172
|
+
when '-l', '--libdir'
|
173
|
+
libdir = ARGV.req_arg
|
174
|
+
when '-b', '--bindir'
|
175
|
+
bindir = ARGV.req_arg
|
176
|
+
when '-r', '--ruby'
|
177
|
+
$ruby = ARGV.req_arg
|
178
|
+
when '-n', '--no_linkify'
|
179
|
+
no_linkify = true
|
180
|
+
when '-s', '--sudo'
|
181
|
+
sudo = 'sudo'
|
182
|
+
when '-h', '--help'
|
183
|
+
help = true
|
184
|
+
else
|
185
|
+
raise "unknown switch #{switch.dump}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
rescue
|
189
|
+
STDERR.puts $!.to_s
|
190
|
+
STDERR.puts usage
|
191
|
+
exit 1
|
192
|
+
end
|
193
|
+
|
194
|
+
if help
|
195
|
+
STDOUT.puts usage
|
196
|
+
exit
|
197
|
+
end
|
198
|
+
|
199
|
+
system "#{ sudo } #{ $ruby } pre-install.rb" if test(?s, 'pre-install.rb')
|
200
|
+
|
201
|
+
unless no_linkify
|
202
|
+
linked = linkify('lib') + linkify('bin')
|
203
|
+
end
|
204
|
+
|
205
|
+
system "#{ $ruby } extconf.rb && make && #{ sudo } make install" if test(?s, 'extconf.rb')
|
206
|
+
|
207
|
+
install_rb(LIBDIR, libdir, LIBDIR_MODE)
|
208
|
+
install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
|
209
|
+
|
210
|
+
if linked
|
211
|
+
linked.each{|path| File::rm_f path}
|
212
|
+
end
|
213
|
+
|
214
|
+
system "#{ sudo } #{ $ruby } post-install.rb" if test(?s, 'post-install.rb')
|
data/lib/fattr.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
module Fattr
|
2
|
+
Fattr::VERSION = '1.0.1' unless Fattr.const_defined?(:VERSION)
|
3
|
+
def self.version() Fattr::VERSION end
|
4
|
+
|
5
|
+
class List < ::Array
|
6
|
+
def << element
|
7
|
+
super
|
8
|
+
self
|
9
|
+
ensure
|
10
|
+
uniq!
|
11
|
+
index!
|
12
|
+
end
|
13
|
+
|
14
|
+
def index!
|
15
|
+
@index ||= Hash.new
|
16
|
+
each{|element| @index[element] = true}
|
17
|
+
end
|
18
|
+
|
19
|
+
def include? element
|
20
|
+
@index ||= Hash.new
|
21
|
+
@index[element] ? true : false
|
22
|
+
end
|
23
|
+
|
24
|
+
def initializers
|
25
|
+
@initializers ||= Hash.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def fattrs *a, &b
|
30
|
+
unless a.empty?
|
31
|
+
returned = Hash.new
|
32
|
+
|
33
|
+
hashes, names = a.partition{|x| Hash === x}
|
34
|
+
names_and_defaults = {}
|
35
|
+
hashes.each{|h| names_and_defaults.update h}
|
36
|
+
names.flatten.compact.each{|name| names_and_defaults.update name => nil}
|
37
|
+
|
38
|
+
initializers = __fattrs__.initializers
|
39
|
+
|
40
|
+
names_and_defaults.each do |name, default|
|
41
|
+
raise NameError, "bad instance variable name '@#{ name }'" if "@#{ name }" =~ %r/[!?=]$/o
|
42
|
+
name = name.to_s
|
43
|
+
|
44
|
+
initialize = b || lambda { default }
|
45
|
+
initializer = lambda do |this|
|
46
|
+
Object.instance_method('instance_eval').bind(this).call &initialize
|
47
|
+
end
|
48
|
+
initializer_id = initializer.object_id
|
49
|
+
__fattrs__.initializers[name] = initializer
|
50
|
+
|
51
|
+
# setter, block invocation caches block
|
52
|
+
module_eval <<-code
|
53
|
+
def #{ name }=(*value, &block)
|
54
|
+
value.unshift block if block
|
55
|
+
@#{ name } = value.first
|
56
|
+
end
|
57
|
+
code
|
58
|
+
|
59
|
+
# getter, providing a value or block causes it to acts as setter
|
60
|
+
module_eval <<-code
|
61
|
+
def #{ name }(*value, &block)
|
62
|
+
value.unshift block if block
|
63
|
+
return self.send('#{ name }=', value.first) unless value.empty?
|
64
|
+
#{ name }! unless defined? @#{ name }
|
65
|
+
@#{ name }
|
66
|
+
end
|
67
|
+
code
|
68
|
+
|
69
|
+
# bang method re-calls any initializer given at declaration time
|
70
|
+
module_eval <<-code
|
71
|
+
def #{ name }!
|
72
|
+
initializer = ObjectSpace._id2ref #{ initializer_id }
|
73
|
+
self.#{ name } = initializer.call(self)
|
74
|
+
@#{ name }
|
75
|
+
end
|
76
|
+
code
|
77
|
+
|
78
|
+
# query simply defers to getter - cast to bool
|
79
|
+
module_eval <<-code
|
80
|
+
def #{ name }?
|
81
|
+
#{ name }
|
82
|
+
end
|
83
|
+
code
|
84
|
+
|
85
|
+
fattrs << name
|
86
|
+
returned[name] = initializer
|
87
|
+
end
|
88
|
+
|
89
|
+
returned
|
90
|
+
else
|
91
|
+
begin
|
92
|
+
__fattr_list__
|
93
|
+
rescue NameError
|
94
|
+
singleton_class =
|
95
|
+
class << self
|
96
|
+
self
|
97
|
+
end
|
98
|
+
klass = self
|
99
|
+
singleton_class.module_eval do
|
100
|
+
fattr_list = List.new
|
101
|
+
define_method('fattr_list'){ klass == self ? fattr_list : raise(NameError) }
|
102
|
+
alias_method '__fattr_list__', 'fattr_list'
|
103
|
+
end
|
104
|
+
__fattr_list__
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
%w( __fattrs__ __fattr__ fattr ).each{|dst| alias_method dst, 'fattrs'}
|
110
|
+
end
|
111
|
+
|
112
|
+
class Module
|
113
|
+
include Fattr
|
114
|
+
end
|
115
|
+
|
116
|
+
class Object
|
117
|
+
def fattrs *a, &b
|
118
|
+
sc =
|
119
|
+
class << self
|
120
|
+
self
|
121
|
+
end
|
122
|
+
sc.fattrs *a, &b
|
123
|
+
end
|
124
|
+
%w( __fattrs__ __fattr__ fattr ).each{|dst| alias_method dst, 'fattrs'}
|
125
|
+
end
|
data/samples/a.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# basic usage is like attr, but note that fattr defines a suite of methods
|
3
|
+
#
|
4
|
+
require 'fattr'
|
5
|
+
|
6
|
+
class C
|
7
|
+
fattr 'a'
|
8
|
+
end
|
9
|
+
|
10
|
+
c = C.new
|
11
|
+
|
12
|
+
c.a = 42
|
13
|
+
p c.a #=> 42
|
14
|
+
p 'forty-two' if c.a? #=> 'forty-two'
|
15
|
+
|
16
|
+
#
|
17
|
+
# fattrs works on object too
|
18
|
+
#
|
19
|
+
o = Object.new
|
20
|
+
o.fattr 'answer' => 42
|
21
|
+
p o.answer #=> 42
|
data/samples/b.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# default values may be given either directly or as a block which will be
|
3
|
+
# evaluated in the context of self. in both cases (value or block) the
|
4
|
+
# default is set only once and only if needed - it's a lazy evaluation. the
|
5
|
+
# 'banger' method can be used to re-initialize a variable at any point whether
|
6
|
+
# or not it's already been initialized.
|
7
|
+
#
|
8
|
+
require 'fattr'
|
9
|
+
|
10
|
+
class C
|
11
|
+
fattr :a => 42
|
12
|
+
fattr(:b){ Float a }
|
13
|
+
end
|
14
|
+
|
15
|
+
c = C.new
|
16
|
+
p c.a #=> 42
|
17
|
+
p c.b #=> 42.0
|
18
|
+
|
19
|
+
c.a = 43
|
20
|
+
p c.a #=> 43
|
21
|
+
c.a!
|
22
|
+
p c.a #=> 42
|
data/samples/c.rb
ADDED
data/samples/d.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#
|
2
|
+
# a nice feature is that all fattrs are enumerated in the class. this,
|
3
|
+
# combined with the fact that the getter method is defined so as to delegate
|
4
|
+
# to the setter when an argument is given, means bulk initialization and/or
|
5
|
+
# fattr traversal is very easy.
|
6
|
+
#
|
7
|
+
require 'fattr'
|
8
|
+
|
9
|
+
class C
|
10
|
+
fattrs %w( x y z )
|
11
|
+
|
12
|
+
def fattrs
|
13
|
+
self.class.fattrs
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
fattrs.each_with_index{|a,i| send a, i}
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
fattrs.inject({}){|h,a| h.update a => send(a)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
to_hash.inspect
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
c = C.new
|
30
|
+
p c.fattrs
|
31
|
+
p c
|
32
|
+
|
33
|
+
c.x 'forty-two'
|
34
|
+
p c.x
|
data/samples/e.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# my favourite element of fattrs is that getters can also be setters.
|
3
|
+
# this allows incredibly clean looking code like
|
4
|
+
#
|
5
|
+
require 'fattr'
|
6
|
+
|
7
|
+
class Config
|
8
|
+
fattrs %w( host port)
|
9
|
+
def initialize(&block) instance_eval &block end
|
10
|
+
end
|
11
|
+
|
12
|
+
conf = Config.new{
|
13
|
+
host 'codeforpeople.org'
|
14
|
+
|
15
|
+
port 80
|
16
|
+
}
|
17
|
+
|
18
|
+
p conf
|
data/samples/f.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# of course fattrs works as well at class/module level as at instance
|
3
|
+
# level
|
4
|
+
#
|
5
|
+
require 'fattr'
|
6
|
+
|
7
|
+
module Logging
|
8
|
+
Level_names = {
|
9
|
+
0 => 'INFO',
|
10
|
+
# ...
|
11
|
+
42 => 'DEBUG',
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
fattr 'level' => 42
|
16
|
+
fattr('level_name'){ Level_names[level] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
p Logging.level
|
21
|
+
p Logging.level_name
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fattr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ara T. Howard
|
8
|
+
autorequire: fattr
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-02-12 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: ara.t.howard@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- gemspec.rb
|
26
|
+
- gen_readme.rb
|
27
|
+
- install.rb
|
28
|
+
- lib
|
29
|
+
- lib/fattr.rb
|
30
|
+
- README
|
31
|
+
- README.tmpl
|
32
|
+
- samples
|
33
|
+
- samples/a.rb
|
34
|
+
- samples/b.rb
|
35
|
+
- samples/c.rb
|
36
|
+
- samples/d.rb
|
37
|
+
- samples/e.rb
|
38
|
+
- samples/f.rb
|
39
|
+
has_rdoc: false
|
40
|
+
homepage: http://codeforpeople.com/lib/ruby/fattr/
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.0.1
|
62
|
+
signing_key:
|
63
|
+
specification_version: 2
|
64
|
+
summary: fattr
|
65
|
+
test_files: []
|
66
|
+
|