vcs 0.1 → 0.2.148
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/Rakefile +17 -3
- data/bin/vcs +57 -34
- data/doc/jamis.rb +564 -0
- data/ruby_ex/abstract.rb +254 -0
- data/ruby_ex/abstract_node.rb +85 -0
- data/ruby_ex/algorithms/simulated_annealing.rb +140 -0
- data/ruby_ex/array_each_pair.rb +18 -0
- data/ruby_ex/ask.rb +101 -0
- data/ruby_ex/attributed_class.rb +302 -0
- data/ruby_ex/cache.rb +373 -0
- data/ruby_ex/checkout.rb +12 -0
- data/ruby_ex/choose.rb +271 -0
- data/ruby_ex/commands.rb +18 -0
- data/ruby_ex/commands/command.rb +401 -0
- data/ruby_ex/commands/datas.rb +16 -0
- data/ruby_ex/commands/datas/data.rb +33 -0
- data/ruby_ex/commands/datas/factory.rb +66 -0
- data/ruby_ex/commands/factory.rb +66 -0
- data/ruby_ex/commands/helpers.rb +67 -0
- data/ruby_ex/commands/pipe.rb +64 -0
- data/ruby_ex/commands/runners.rb +17 -0
- data/ruby_ex/commands/runners/exec.rb +49 -0
- data/ruby_ex/commands/runners/fork.rb +97 -0
- data/ruby_ex/commands/runners/runner.rb +107 -0
- data/ruby_ex/commands/seq.rb +27 -0
- data/ruby_ex/config_file.rb +96 -0
- data/ruby_ex/const_regexp.rb +59 -0
- data/ruby_ex/daemon.rb +134 -0
- data/ruby_ex/diff.rb +667 -0
- data/ruby_ex/dlogger.rb +62 -0
- data/ruby_ex/drb/dispatcher.rb +252 -0
- data/ruby_ex/drb/dispatcher_server_test.rb +29 -0
- data/ruby_ex/drb/drb_observable.rb +97 -0
- data/ruby_ex/drb/drb_observable_pool.rb +27 -0
- data/ruby_ex/drb/drb_service.rb +43 -0
- data/ruby_ex/drb/drb_undumped_attributes.rb +55 -0
- data/ruby_ex/drb/drb_undumped_indexed_object.rb +54 -0
- data/ruby_ex/drb/insecure_protected_methods.rb +103 -0
- data/ruby_ex/drb/session_client_test.rb +40 -0
- data/ruby_ex/drb/session_manager.rb +246 -0
- data/ruby_ex/drb/session_server.rb +53 -0
- data/ruby_ex/dtime.rb +143 -0
- data/ruby_ex/dumpable_proc.rb +63 -0
- data/ruby_ex/exception.rb +32 -0
- data/ruby_ex/filetype.rb +229 -0
- data/ruby_ex/fileutils_ex.rb +44 -0
- data/ruby_ex/fold.rb +58 -0
- data/ruby_ex/generate_id.rb +44 -0
- data/ruby_ex/hookable.rb +262 -0
- data/ruby_ex/hooker.rb +54 -0
- data/ruby_ex/inactive_timeout.rb +137 -0
- data/ruby_ex/indexed_node.rb +66 -0
- data/ruby_ex/io_marshal.rb +100 -0
- data/ruby_ex/ioo.rb +194 -0
- data/ruby_ex/labeled_node.rb +63 -0
- data/ruby_ex/logger_observer.rb +23 -0
- data/ruby_ex/md5sum.rb +66 -0
- data/ruby_ex/mktemp.rb +208 -0
- data/ruby_ex/module/attr_once.rb +36 -0
- data/ruby_ex/module/autoload_tree.rb +75 -0
- data/ruby_ex/module/hierarchy.rb +335 -0
- data/ruby_ex/module/instance_method_visibility.rb +73 -0
- data/ruby_ex/module_ex.rb +11 -0
- data/ruby_ex/node.rb +80 -0
- data/ruby_ex/object_monitor.rb +145 -0
- data/ruby_ex/object_monitor_activity.rb +33 -0
- data/ruby_ex/observable.rb +140 -0
- data/ruby_ex/observable_pool.rb +293 -0
- data/ruby_ex/orderedhash.rb +252 -0
- data/ruby_ex/pathname_ex.rb +152 -0
- data/ruby_ex/pp_hierarchy.rb +29 -0
- data/ruby_ex/pseudo_cache.rb +190 -0
- data/ruby_ex/queue.rb +56 -0
- data/ruby_ex/random_generators.rb +25 -0
- data/ruby_ex/random_generators/random_generator.rb +31 -0
- data/ruby_ex/random_generators/ruby.rb +23 -0
- data/ruby_ex/safe_eval.rb +348 -0
- data/ruby_ex/sendmail.rb +215 -0
- data/ruby_ex/service_manager.rb +121 -0
- data/ruby_ex/session/administrable.rb +120 -0
- data/ruby_ex/session/client.rb +153 -0
- data/ruby_ex/session/const.rb +18 -0
- data/ruby_ex/session/dispatcher.rb +184 -0
- data/ruby_ex/session/error.rb +21 -0
- data/ruby_ex/session/fetchable.rb +57 -0
- data/ruby_ex/session/fetcher.rb +62 -0
- data/ruby_ex/session/hookable.rb +26 -0
- data/ruby_ex/session/profile.rb +110 -0
- data/ruby_ex/session/server.rb +582 -0
- data/ruby_ex/session/test/administrable_test.rb +337 -0
- data/ruby_ex/session/test/basic_test.rb +523 -0
- data/ruby_ex/session/test/dispatcher_test.rb +409 -0
- data/ruby_ex/session/test/fetchable_test.rb +119 -0
- data/ruby_ex/session/test/sub_server_test.rb +188 -0
- data/ruby_ex/shuffle.rb +30 -0
- data/ruby_ex/spring.rb +136 -0
- data/ruby_ex/spring_set.rb +137 -0
- data/ruby_ex/string_ex.rb +28 -0
- data/ruby_ex/symtbl.rb +106 -0
- data/ruby_ex/synflow.rb +474 -0
- data/ruby_ex/test/unit/ui/yaml/testrunner.rb +164 -0
- data/ruby_ex/thread_mutex.rb +10 -0
- data/ruby_ex/timeout_ex.rb +81 -0
- data/ruby_ex/top_down.rb +73 -0
- data/ruby_ex/trace.rb +26 -0
- data/ruby_ex/uri/druby.rb +81 -0
- data/ruby_ex/uri/file.rb +65 -0
- data/ruby_ex/uri/ftp_ex.rb +37 -0
- data/ruby_ex/uri/http_ex.rb +43 -0
- data/ruby_ex/uri/ssh.rb +92 -0
- data/ruby_ex/uri/svn.rb +118 -0
- data/ruby_ex/uri_ex.rb +45 -0
- data/ruby_ex/verbose_object.rb +30 -0
- data/ruby_ex/version.rb +66 -0
- data/ruby_ex/yaml/basenode_ext.rb +63 -0
- data/ruby_ex/yaml/chop_header.rb +23 -0
- data/ruby_ex/yaml/transform.rb +449 -0
- data/ruby_ex/yaml/yregexpath.rb +76 -0
- data/src/changelog.rb +28 -18
- data/src/conflict.rb +20 -0
- data/src/diff.rb +18 -0
- data/src/diffstat.rb +9 -3
- data/src/last_changed_date.rb +18 -0
- data/src/mail.rb +33 -65
- data/src/message.rb +15 -9
- data/src/mycommit.rb +29 -14
- data/src/news.rb +24 -3
- data/src/status.rb +17 -0
- data/src/svn.rb +2 -2
- data/src/vcs.rb +24 -3
- metadata +124 -5
- data/lrdetools.rb +0 -12
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
|
|
2
|
+
# Author: Nicolas Despres <polrop@lrde.epita.fr>.
|
|
3
|
+
# License: Gnu General Public License.
|
|
4
|
+
|
|
5
|
+
# $LastChangedBy: polrop $
|
|
6
|
+
# $Id: cache.rb 173 2005-03-29 09:41:46Z polrop $
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Array
|
|
10
|
+
|
|
11
|
+
# Basically, do the same as each_with_index but yield the arguments in
|
|
12
|
+
# reverse order. Thus an array can be assume in certain cases to a hash.
|
|
13
|
+
def each_pair(&block)
|
|
14
|
+
each_with_index { |x, i| block[i, x] }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end # class Array
|
|
18
|
+
|
data/ruby_ex/ask.rb
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Author: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
2
|
+
# Copyright: Copyright (c) 2004, 2005 Nicolas Pouillard. All rights reserved.
|
|
3
|
+
# License: GNU General Public License (GPL).
|
|
4
|
+
|
|
5
|
+
# $LastChangedBy: ertai $
|
|
6
|
+
# $Id: ask.rb 187 2005-04-03 00:11:56Z ertai $
|
|
7
|
+
|
|
8
|
+
ANSWERS = [ :y, :n ]
|
|
9
|
+
ANSWER_NOT_VALID = 'Not a valid answer, please answer correctly'
|
|
10
|
+
|
|
11
|
+
# `ask', ask the user to answer, to your question.
|
|
12
|
+
#
|
|
13
|
+
# Example:
|
|
14
|
+
# ask('Commiting, are you sure', :n)
|
|
15
|
+
#
|
|
16
|
+
# produce => Commiting, are you sure (y/N):
|
|
17
|
+
# and wait your answer.
|
|
18
|
+
def ask ( aQuestion, theDefaultAnswer=:y, cin=STDIN, cout=STDOUT, cerr=STDERR )
|
|
19
|
+
|
|
20
|
+
yn = case theDefaultAnswer
|
|
21
|
+
when :y then ' [Y/n]: '
|
|
22
|
+
when :n then ' [y/N]: '
|
|
23
|
+
else raise ArgumentError, "not valid default answer #{theDefaultAnswer}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
loop do
|
|
27
|
+
cout.print aQuestion, yn
|
|
28
|
+
cout.flush
|
|
29
|
+
|
|
30
|
+
answer = cin.readline.chomp.downcase
|
|
31
|
+
|
|
32
|
+
return theDefaultAnswer if answer.empty?
|
|
33
|
+
|
|
34
|
+
answer = answer.to_sym
|
|
35
|
+
|
|
36
|
+
return answer if ANSWERS.include? answer
|
|
37
|
+
|
|
38
|
+
cerr.puts ANSWER_NOT_VALID
|
|
39
|
+
cout.puts
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if defined? TEST_MODE or $0 == __FILE__
|
|
45
|
+
|
|
46
|
+
require 'test/unit'
|
|
47
|
+
class AskTest < Test::Unit::TestCase
|
|
48
|
+
|
|
49
|
+
def ask_checker ( question, default, answer, ref, out, err )
|
|
50
|
+
require 'stringio'
|
|
51
|
+
cin, cout, cerr = StringIO.new, StringIO.new, StringIO.new
|
|
52
|
+
cin.puts answer
|
|
53
|
+
cin.rewind
|
|
54
|
+
res = ask(question, default, cin, cout, cerr)
|
|
55
|
+
cout.rewind
|
|
56
|
+
cerr.rewind
|
|
57
|
+
assert_equal(res, ref, 'bad return value')
|
|
58
|
+
assert_equal(cout.readlines.join, out, 'bad standard output')
|
|
59
|
+
assert_equal(cerr.readlines.join, err, 'bad error output')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_bad_default
|
|
63
|
+
assert_raise(ArgumentError) { ask_checker('Q', :foo, '', :y, [], []) }
|
|
64
|
+
assert_nothing_raised do
|
|
65
|
+
ask_checker('Q', :y, 'y', :y, 'Q [Y/n]: ', '')
|
|
66
|
+
end
|
|
67
|
+
assert_nothing_raised do
|
|
68
|
+
ask_checker('Q', :n, 'y', :y, 'Q [y/N]: ', '')
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_valid_default_yes
|
|
73
|
+
ask_checker('Q', :y, 'y', :y, 'Q [Y/n]: ', '')
|
|
74
|
+
ask_checker('Q', :y, 'Y', :y, 'Q [Y/n]: ', '')
|
|
75
|
+
ask_checker('Q', :y, 'n', :n, 'Q [Y/n]: ', '')
|
|
76
|
+
ask_checker('Q', :y, 'N', :n, 'Q [Y/n]: ', '')
|
|
77
|
+
ask_checker('Q', :y, '', :y, 'Q [Y/n]: ', '')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_valid_default_no
|
|
81
|
+
ask_checker('Q', :n, 'y', :y, 'Q [y/N]: ', '')
|
|
82
|
+
ask_checker('Q', :n, 'Y', :y, 'Q [y/N]: ', '')
|
|
83
|
+
ask_checker('Q', :n, 'n', :n, 'Q [y/N]: ', '')
|
|
84
|
+
ask_checker('Q', :n, 'N', :n, 'Q [y/N]: ', '')
|
|
85
|
+
ask_checker('Q', :n, '', :n, 'Q [y/N]: ', '')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_invalid_answer
|
|
89
|
+
ask_checker('Q', :n, "bad\ny", :y,
|
|
90
|
+
"Q [y/N]: \nQ [y/N]: ", ANSWER_NOT_VALID + "\n")
|
|
91
|
+
ask_checker('Q', :y, "ad\n\n", :y,
|
|
92
|
+
"Q [Y/n]: \nQ [Y/n]: ", ANSWER_NOT_VALID + "\n")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_ask1
|
|
96
|
+
ask_checker('Q', :y, 'y', :y, 'Q [Y/n]: ', '')
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end # class AskTest
|
|
100
|
+
|
|
101
|
+
end
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Copyright:: Copyright (c) 2004, 2005 Nicolas Pouillard. All rights reserved.
|
|
2
|
+
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
|
3
|
+
# License:: Gnu General Public License.
|
|
4
|
+
# Revision:: $Id: attributed_class.rb 212 2005-05-05 20:07:23Z ertai $
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Extension to have inherited, class attributes,
|
|
8
|
+
# Where each class have his own attributes set.
|
|
9
|
+
module AttributedClass
|
|
10
|
+
|
|
11
|
+
class Attribute
|
|
12
|
+
|
|
13
|
+
attr_reader :name, :descr, :klass, :default_proc
|
|
14
|
+
attr_accessor :default
|
|
15
|
+
|
|
16
|
+
@@default_proc = nil
|
|
17
|
+
|
|
18
|
+
def initialize ( name, descr, *args, &block )
|
|
19
|
+
raise ArgumentError, 'need a symbol' unless name.is_a? Symbol
|
|
20
|
+
@name, @descr = name, descr
|
|
21
|
+
@default, @klass = nil, nil
|
|
22
|
+
@mandatory = false
|
|
23
|
+
@visible = true
|
|
24
|
+
if block_given?
|
|
25
|
+
@default_proc = block
|
|
26
|
+
else
|
|
27
|
+
@default_proc = @@default_proc
|
|
28
|
+
end
|
|
29
|
+
other = []
|
|
30
|
+
|
|
31
|
+
args.each do |arg|
|
|
32
|
+
case arg
|
|
33
|
+
when :mandatory then @mandatory = true
|
|
34
|
+
when :invisible then @visible = false
|
|
35
|
+
else other << arg
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
case other.size
|
|
40
|
+
when 1
|
|
41
|
+
if other[0].is_a? Class
|
|
42
|
+
@klass = [other[0]]
|
|
43
|
+
elsif other[0].is_a? Array and other[0].all? { |k| k.is_a? Class }
|
|
44
|
+
@klass = other[0]
|
|
45
|
+
else
|
|
46
|
+
@default = other[0]
|
|
47
|
+
end
|
|
48
|
+
when 2
|
|
49
|
+
a, b = other
|
|
50
|
+
if a.is_a? Class and b.is_a? a
|
|
51
|
+
@klass, @default = [a], b
|
|
52
|
+
elsif a.is_a? Array and
|
|
53
|
+
a.all? { |k| k.is_a? Class } and
|
|
54
|
+
a.any? { |k| b.is_a? k }
|
|
55
|
+
@klass, @default = a, b
|
|
56
|
+
elsif b.is_a? Class and a.is_a? b
|
|
57
|
+
@klass, @default = [b], a
|
|
58
|
+
elsif b.is_a? Array and
|
|
59
|
+
b.all? { |k| k.is_a? Class } and
|
|
60
|
+
b.any? { |k| a.is_a? k }
|
|
61
|
+
@klass, @default = b, a
|
|
62
|
+
else
|
|
63
|
+
raise ArgumentError, 'need a Class and a default value'
|
|
64
|
+
end
|
|
65
|
+
when 0
|
|
66
|
+
else raise ArgumentError, 'too many values'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# removed because it causes some probleme when distributed such a class
|
|
70
|
+
#[@name, @descr, @default].each { |x| x.freeze }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def visible?
|
|
74
|
+
@visible
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def invisible?
|
|
78
|
+
!@visible
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def mandatory?
|
|
82
|
+
@mandatory
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def valid? ( anObject )
|
|
86
|
+
return false if mandatory? and anObject.nil?
|
|
87
|
+
@klass.nil? or @klass.empty? or @klass.any? { |k| anObject.is_a? k }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def self.set_default_proc ( &block )
|
|
92
|
+
@@default_proc = block
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def help ( output=STDERR )
|
|
97
|
+
output << "#@name: #@descr"
|
|
98
|
+
output << " [#@klass]" unless @klass.nil?
|
|
99
|
+
other = []
|
|
100
|
+
other << 'mandatory' if mandatory?
|
|
101
|
+
other << 'invisible' if invisible?
|
|
102
|
+
output << " (#{other.join(', ')})" unless other.empty?
|
|
103
|
+
output
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def inspect
|
|
107
|
+
help('#<') + '>'
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end # class Attribute
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class MethodAttribute < Attribute
|
|
114
|
+
VISIBILITIES = [ :private, :protected, :public, :unknown ]
|
|
115
|
+
attr_reader :visibility
|
|
116
|
+
def private?
|
|
117
|
+
@visibility == :private
|
|
118
|
+
end
|
|
119
|
+
def protected?
|
|
120
|
+
@visibility == :protected
|
|
121
|
+
end
|
|
122
|
+
def public?
|
|
123
|
+
@visibility == :public
|
|
124
|
+
end
|
|
125
|
+
def initialize ( name, descr, *args )
|
|
126
|
+
vis = args.find { |arg| VISIBILITIES.include? arg }
|
|
127
|
+
args.delete(vis) unless vis.nil?
|
|
128
|
+
@visibility = vis || :unknown
|
|
129
|
+
super(name, descr, *args)
|
|
130
|
+
end
|
|
131
|
+
end # class MethodAttribute
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class AttributeError < Exception
|
|
135
|
+
|
|
136
|
+
def initialize ( anObject, anAttr, val=nil )
|
|
137
|
+
if val.nil?
|
|
138
|
+
super "Missing the #{anAttr.name} attribute for `#{anObject}'"
|
|
139
|
+
else
|
|
140
|
+
super "Attribute #{anAttr.name} wait for a `#{anAttr.klass}' not " +
|
|
141
|
+
"`#{val}' in #{anObject}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
end # class AttributeError
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def self.included ( aClass )
|
|
149
|
+
|
|
150
|
+
aClass.module_eval do
|
|
151
|
+
|
|
152
|
+
def initialize_attributes ( *default_proc_args )
|
|
153
|
+
self.class.attributes.each do |attr|
|
|
154
|
+
next if attr.is_a? MethodAttribute
|
|
155
|
+
if attr.default_proc.nil? and attr.default.nil?
|
|
156
|
+
instance_variable_set("@#{attr.name}", nil)
|
|
157
|
+
else
|
|
158
|
+
unless attr.default_proc.nil?
|
|
159
|
+
pr = attr.default_proc
|
|
160
|
+
if pr.arity == default_proc_args.size
|
|
161
|
+
default = pr[*default_proc_args]
|
|
162
|
+
else
|
|
163
|
+
default = pr[attr, *default_proc_args]
|
|
164
|
+
end
|
|
165
|
+
attr.default = default
|
|
166
|
+
end
|
|
167
|
+
send "#{attr.name}=", attr.default
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def self.set_default_proc ( &block )
|
|
173
|
+
Attribute.set_default_proc(&block)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def each_attribute ( &block )
|
|
177
|
+
self.class.attributes.each do |attr|
|
|
178
|
+
block[attr, send(attr.name)]
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def each_variable_attribute ( &block )
|
|
183
|
+
self.class.attributes.each do |attr|
|
|
184
|
+
next if attr.is_a? MethodAttribute
|
|
185
|
+
block[attr, send(attr.name)]
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def each_method_attribute ( &block )
|
|
190
|
+
self.class.attributes.each do |attr|
|
|
191
|
+
next unless attr.is_a? MethodAttribute
|
|
192
|
+
block[attr, send(attr.name)]
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def check_attribute ( attr, anObject )
|
|
197
|
+
unless attr.valid? anObject
|
|
198
|
+
raise AttributeError.new(self, attr, anObject)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def check_attributes
|
|
203
|
+
each_variable_attribute(&method(:check_attribute))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def self.help ( output=STDERR )
|
|
207
|
+
output.puts "#{self.to_s}:"
|
|
208
|
+
attributes.each do |attr|
|
|
209
|
+
output << ' '
|
|
210
|
+
attr.help(output)
|
|
211
|
+
output.puts '.'
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def self.attributes
|
|
216
|
+
unless defined? @attributes
|
|
217
|
+
if superclass and superclass.respond_to? :attributes
|
|
218
|
+
@attributes = superclass.attributes.dup
|
|
219
|
+
else
|
|
220
|
+
@attributes = []
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
@attributes
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def self.attribute ( name, descr, *args, &block )
|
|
227
|
+
attr = Attribute.new(name, descr, *args, &block)
|
|
228
|
+
unless method_defined? name
|
|
229
|
+
if att = get_attribute(name, MethodAttribute)
|
|
230
|
+
attributes.delete(att)
|
|
231
|
+
end
|
|
232
|
+
attr_reader name
|
|
233
|
+
end
|
|
234
|
+
meth = "#{name}="
|
|
235
|
+
unless method_defined? meth
|
|
236
|
+
if att = get_attribute(meth, MethodAttribute)
|
|
237
|
+
attributes.delete(att)
|
|
238
|
+
end
|
|
239
|
+
attr_writer name
|
|
240
|
+
end
|
|
241
|
+
attributes << attr
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def self.method_attribute ( name, descr, *args, &block )
|
|
245
|
+
# unless method_defined? name
|
|
246
|
+
# raise ArgumentError, "Undefined method #{name}"
|
|
247
|
+
# end
|
|
248
|
+
attr = MethodAttribute.new(name, descr, *args, &block)
|
|
249
|
+
attributes << attr
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.get_attribute ( name, type=nil )
|
|
253
|
+
if type.nil?
|
|
254
|
+
attributes.find { |attr| attr.name == name }
|
|
255
|
+
else
|
|
256
|
+
attributes.find { |attr| attr.name == name && attr.is_a?(type) }
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
end # module AttributedClass
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if defined? TEST_MODE or $0 == __FILE__
|
|
269
|
+
|
|
270
|
+
require 'test/unit'
|
|
271
|
+
class AttributedClassTest < Test::Unit::TestCase
|
|
272
|
+
|
|
273
|
+
def test_simple
|
|
274
|
+
c = Class.new
|
|
275
|
+
assert_nothing_raised do
|
|
276
|
+
c.module_eval { include AttributedClass }
|
|
277
|
+
end
|
|
278
|
+
assert_equal([], c.attributes)
|
|
279
|
+
assert_nothing_raised do
|
|
280
|
+
c.module_eval { attribute :foo, 'foofoo', 42 }
|
|
281
|
+
end
|
|
282
|
+
cc, d, dd = nil, nil, nil
|
|
283
|
+
assert_nothing_raised { cc = c.new }
|
|
284
|
+
assert_equal(nil, cc.foo)
|
|
285
|
+
assert_nothing_raised { cc.initialize_attributes }
|
|
286
|
+
assert_equal(42, cc.foo)
|
|
287
|
+
assert_nothing_raised { cc.foo = 32 }
|
|
288
|
+
assert_equal(32, cc.foo)
|
|
289
|
+
assert_nothing_raised { d = Class.new(c) }
|
|
290
|
+
assert_nothing_raised do
|
|
291
|
+
d.module_eval { attribute :bar, 'foofoo', :mandatory }
|
|
292
|
+
end
|
|
293
|
+
assert_nothing_raised { dd = d.new }
|
|
294
|
+
assert_nothing_raised { dd.initialize_attributes }
|
|
295
|
+
assert_raise(AttributedClass::AttributeMissingError) do
|
|
296
|
+
dd.check_attributes
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
end # class AttributedClassTest
|
|
301
|
+
|
|
302
|
+
end
|
data/ruby_ex/cache.rb
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
|
|
2
|
+
# Author: Nicolas Despres <polrop@lrde.epita.fr>.
|
|
3
|
+
# License: Gnu General Public License.
|
|
4
|
+
|
|
5
|
+
# $LastChangedBy: ertai $
|
|
6
|
+
# $Id: cache.rb 186 2005-04-03 00:07:45Z ertai $
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
require 'md5'
|
|
11
|
+
require 'tempfile'
|
|
12
|
+
require 'fileutils'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Cache
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Constants
|
|
19
|
+
#
|
|
20
|
+
REPOSITORY = '/var/cache/ruby_ex_cache'
|
|
21
|
+
MAX_SIZE = 50 * 1024 * 1024 # 50 MB
|
|
22
|
+
BLOCK_SIZE = 1024 # octets
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Attributs
|
|
26
|
+
#
|
|
27
|
+
attr_reader :repository, :max_size, :size
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Constructor
|
|
31
|
+
#
|
|
32
|
+
def initialize(repository=REPOSITORY, max_size=MAX_SIZE)
|
|
33
|
+
@repository = Pathname.new(repository)
|
|
34
|
+
@max_size = check_max_size(max_size)
|
|
35
|
+
create
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Methods
|
|
40
|
+
#
|
|
41
|
+
def create
|
|
42
|
+
@size = 0
|
|
43
|
+
@atime = []
|
|
44
|
+
if @repository.directory?
|
|
45
|
+
@repository.each_entry do |p|
|
|
46
|
+
next if p.to_s =~ /^\./
|
|
47
|
+
full_p = @repository + p
|
|
48
|
+
@atime << full_p
|
|
49
|
+
@size += full_p.size
|
|
50
|
+
end
|
|
51
|
+
@atime.sort! { |a, b| a.atime <=> b.atime }
|
|
52
|
+
else
|
|
53
|
+
@repository.mkdir
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def clear
|
|
58
|
+
@repository.each_entry do |p|
|
|
59
|
+
(@repository + p).delete unless p.to_s =~ /^\./
|
|
60
|
+
end
|
|
61
|
+
@size = 0
|
|
62
|
+
@atime.clear
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def recreate
|
|
66
|
+
clear
|
|
67
|
+
create
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def open(md5sum=nil, mode='r', perm=0644, &block)
|
|
71
|
+
case mode
|
|
72
|
+
when 'r': read(md5sum, &block)
|
|
73
|
+
when 'w': write(perm, &block)
|
|
74
|
+
else
|
|
75
|
+
raise(ArgumentError, "`#{mode}' - bad mode")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def read(md5sum, &block)
|
|
80
|
+
p = @repository + md5sum.to_s
|
|
81
|
+
p.open('r', &block)
|
|
82
|
+
@atime.delete(p)
|
|
83
|
+
@atime << p
|
|
84
|
+
p
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def write(perm=0644, &block)
|
|
88
|
+
tmp = nil
|
|
89
|
+
p = nil
|
|
90
|
+
begin
|
|
91
|
+
md5 = Digest::MD5.new
|
|
92
|
+
tmp = Tempfile.new('cache')
|
|
93
|
+
new_size = 0
|
|
94
|
+
while (str = block[])
|
|
95
|
+
tmp.write(str)
|
|
96
|
+
md5 << str
|
|
97
|
+
new_size += str.size
|
|
98
|
+
end
|
|
99
|
+
tmp.close
|
|
100
|
+
p = @repository + md5.to_s
|
|
101
|
+
if p.exist?
|
|
102
|
+
@atime.delete(p)
|
|
103
|
+
else
|
|
104
|
+
@size += new_size
|
|
105
|
+
end
|
|
106
|
+
FileUtils.move(tmp.path, p)
|
|
107
|
+
p.chmod(perm)
|
|
108
|
+
rescue
|
|
109
|
+
tmp.delete if tmp
|
|
110
|
+
raise
|
|
111
|
+
end
|
|
112
|
+
@atime << p
|
|
113
|
+
adjust_size
|
|
114
|
+
p
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def import(filename)
|
|
118
|
+
p = nil
|
|
119
|
+
File.open(filename) do |f|
|
|
120
|
+
p = write(File.stat(filename).mode) { f.eof? ? nil : f.read(BLOCK_SIZE) }
|
|
121
|
+
end
|
|
122
|
+
p
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def export(md5sum, dst_filename)
|
|
126
|
+
p = @repository + md5sum.to_s
|
|
127
|
+
FileUtils.copy_entry(p, dst_filename)
|
|
128
|
+
update_entry(p)
|
|
129
|
+
p
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def present?(md5sum)
|
|
133
|
+
p = @repository + md5sum.to_s
|
|
134
|
+
p.exist? ? p : nil;
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def each
|
|
138
|
+
@atime.each { |p| yield(p) }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def [](index)
|
|
142
|
+
@atime[index]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def max_size=(max_size)
|
|
146
|
+
@max_size = check_max_size(max_size)
|
|
147
|
+
adjust_size
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def nb_files
|
|
151
|
+
@atime.size
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
protected
|
|
155
|
+
def adjust_size
|
|
156
|
+
while @size > @max_size
|
|
157
|
+
p = @atime.shift
|
|
158
|
+
@size -= p.size
|
|
159
|
+
p.delete
|
|
160
|
+
end
|
|
161
|
+
@size
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
protected
|
|
165
|
+
def check_max_size(max_size)
|
|
166
|
+
unless max_size > 0
|
|
167
|
+
raise(ArgumentError, "`#{max_size}' - bad cache maximum size")
|
|
168
|
+
end
|
|
169
|
+
max_size
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
end # class Cache
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
#
|
|
176
|
+
# Unit test suite
|
|
177
|
+
#
|
|
178
|
+
if defined? TEST_MODE or __FILE__ == $0
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
require 'test/unit/ui/yaml/testrunner'
|
|
182
|
+
require 'md5sum'
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class CacheTest < Test::Unit::TestCase
|
|
186
|
+
|
|
187
|
+
#
|
|
188
|
+
# Tests
|
|
189
|
+
#
|
|
190
|
+
def test_simple
|
|
191
|
+
tmp_cache = nil
|
|
192
|
+
begin
|
|
193
|
+
tmp_cache = Tempfile.new('cache')
|
|
194
|
+
repo = Pathname.new(tmp_cache.path)
|
|
195
|
+
tmp_cache.delete
|
|
196
|
+
cache = nil
|
|
197
|
+
assert_nothing_raised { cache = Cache.new(repo) }
|
|
198
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
199
|
+
assert_nothing_raised { cache.create }
|
|
200
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
201
|
+
assert_nothing_raised { cache.clear }
|
|
202
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
203
|
+
assert_nothing_raised { cache.create }
|
|
204
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
205
|
+
assert_raises(Errno::ENOENT) { cache.open('toto', 'r') { |f| f.gets } }
|
|
206
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
207
|
+
assert_equal(0, cache.size)
|
|
208
|
+
|
|
209
|
+
data = [ "toto\n", "tata\n" ]
|
|
210
|
+
p1 = cache.open(nil, 'w') do
|
|
211
|
+
data.shift
|
|
212
|
+
end
|
|
213
|
+
assert_equal(File.md5sum(p1.to_s).to_s, p1.basename.to_s, 'bad md5 open')
|
|
214
|
+
assert_equal(1, cache.nb_files, 'bad nb_files')
|
|
215
|
+
assert_equal(p1, cache[0])
|
|
216
|
+
assert_equal(10, cache.size)
|
|
217
|
+
assert_equal(p1, cache.present?(p1.basename))
|
|
218
|
+
tmp_p = cache.read(p1) do |f|
|
|
219
|
+
assert_equal("toto\n", f.gets)
|
|
220
|
+
assert_equal("tata\n", f.gets)
|
|
221
|
+
assert(f.eof?)
|
|
222
|
+
end
|
|
223
|
+
assert_equal(tmp_p, p1)
|
|
224
|
+
check_atime(cache)
|
|
225
|
+
assert_equal(1, cache.nb_files, 'bad nb_files')
|
|
226
|
+
assert(cache.present?(p1))
|
|
227
|
+
assert_equal(p1, cache[0])
|
|
228
|
+
assert(! cache.present?('foo'))
|
|
229
|
+
|
|
230
|
+
tmp_file = Tempfile.new('cache')
|
|
231
|
+
system("man printf > #{tmp_file.path} 2> /dev/null")
|
|
232
|
+
md5 = File.md5sum(tmp_file.path)
|
|
233
|
+
p2 = cache.import(tmp_file.path)
|
|
234
|
+
assert_equal(md5.to_s, p2.basename.to_s, 'bad name after import')
|
|
235
|
+
assert(p2.exist?, 'file do not exist after import')
|
|
236
|
+
assert(FileUtils.cmp(tmp_file.path, p2.to_s), 'bad import')
|
|
237
|
+
assert_equal(md5.to_s, File.md5sum(p2.to_s).to_s, 'bad md5 import')
|
|
238
|
+
tmp_file.delete
|
|
239
|
+
assert_equal(2, cache.nb_files, 'bad nb_files')
|
|
240
|
+
assert_equal(p2, cache[1])
|
|
241
|
+
check_atime(cache)
|
|
242
|
+
|
|
243
|
+
data = [ "foo\n", "bar\n" ]
|
|
244
|
+
p3 = cache.open(nil, 'w') do
|
|
245
|
+
data.shift
|
|
246
|
+
end
|
|
247
|
+
assert_equal(File.md5sum(p3.to_s).to_s, p3.basename.to_s, 'bad md5 open')
|
|
248
|
+
assert_equal(3, cache.nb_files, 'bad nb_files')
|
|
249
|
+
assert_equal(p3, cache[2])
|
|
250
|
+
assert_equal(p3, cache.present?(p3.basename))
|
|
251
|
+
|
|
252
|
+
assert_equal(p1, cache[0], 'p1 first')
|
|
253
|
+
assert_equal(p2, cache[1], 'p2 second')
|
|
254
|
+
assert_equal(p3, cache[2], 'p3 third')
|
|
255
|
+
assert_equal(p1, cache.read(p1) { |f| f.gets })
|
|
256
|
+
assert_equal(p2, cache[0], 'p2 first')
|
|
257
|
+
assert_equal(p3, cache[1], 'p3 second')
|
|
258
|
+
assert_equal(p1, cache[2], 'p1 third')
|
|
259
|
+
check_atime(cache)
|
|
260
|
+
assert_equal(3, cache.nb_files, 'bad nb_files')
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
new_cache = nil
|
|
264
|
+
assert_nothing_raised { new_cache = Cache.new(repo) }
|
|
265
|
+
assert_equal(3, new_cache.nb_files, 'bad nb_files in new cache')
|
|
266
|
+
check_atime(new_cache)
|
|
267
|
+
assert_equal(cache.size, new_cache.size)
|
|
268
|
+
|
|
269
|
+
assert_equal(3, cache.nb_files, 'bad nb_files')
|
|
270
|
+
assert(cache.size > 1024)
|
|
271
|
+
cache.max_size = 1024
|
|
272
|
+
assert(cache.size < 1024)
|
|
273
|
+
assert_equal(p1.size + p3.size, cache.size)
|
|
274
|
+
assert_equal(2, cache.nb_files, 'bad nb_files')
|
|
275
|
+
assert_equal(p3, cache[0], 'p3 first')
|
|
276
|
+
assert_equal(p1, cache[1], 'p1 second')
|
|
277
|
+
|
|
278
|
+
cache.clear
|
|
279
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
280
|
+
assert_equal(0, cache.size)
|
|
281
|
+
ensure
|
|
282
|
+
repo.rmtree if repo
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def test_write_twice_the_same
|
|
287
|
+
tmp_cache = nil
|
|
288
|
+
begin
|
|
289
|
+
tmp_cache = Tempfile.new('cache')
|
|
290
|
+
repo = Pathname.new(tmp_cache.path)
|
|
291
|
+
tmp_cache.delete
|
|
292
|
+
cache = nil
|
|
293
|
+
assert_nothing_raised { cache = Cache.new(repo) }
|
|
294
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
295
|
+
assert_equal(0, cache.size)
|
|
296
|
+
|
|
297
|
+
data = [ "toto\n", "tata\n" ]
|
|
298
|
+
d = data.dup
|
|
299
|
+
p1 = cache.open(nil, 'w') do
|
|
300
|
+
d.shift
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
assert_equal(File.md5sum(p1.to_s).to_s, p1.basename.to_s, 'bad md5 open')
|
|
304
|
+
assert_equal(1, cache.nb_files, 'bad nb_files')
|
|
305
|
+
assert_equal(p1, cache[0])
|
|
306
|
+
assert_equal(10, cache.size)
|
|
307
|
+
assert_equal(p1, cache.present?(p1.basename))
|
|
308
|
+
|
|
309
|
+
data = [ "toto\n", "tata\n" ]
|
|
310
|
+
d = data.dup
|
|
311
|
+
p2 = cache.open(nil, 'w') do
|
|
312
|
+
d.shift
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
assert_equal(File.md5sum(p1.to_s).to_s, p1.basename.to_s, 'bad md5 open')
|
|
316
|
+
assert_equal(1, cache.nb_files, 'bad nb_files')
|
|
317
|
+
assert_equal(p1, cache[0])
|
|
318
|
+
assert_equal(10, cache.size)
|
|
319
|
+
assert_equal(p1, cache.present?(p1.basename))
|
|
320
|
+
ensure
|
|
321
|
+
repo.rmtree if repo
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def test_nested_write
|
|
326
|
+
tmp_cache = nil
|
|
327
|
+
begin
|
|
328
|
+
tmp_cache = Tempfile.new('cache')
|
|
329
|
+
repo = Pathname.new(tmp_cache.path)
|
|
330
|
+
tmp_cache.delete
|
|
331
|
+
cache = nil
|
|
332
|
+
assert_nothing_raised { cache = Cache.new(repo) }
|
|
333
|
+
assert_equal(0, cache.nb_files, 'bad nb_files')
|
|
334
|
+
assert_equal(0, cache.size)
|
|
335
|
+
|
|
336
|
+
data = [ "toto\n", "tata\n" ]
|
|
337
|
+
d1 = data.dup
|
|
338
|
+
p1 = cache.open(nil, 'w') do
|
|
339
|
+
d2 = data.dup
|
|
340
|
+
p2 = cache.open(nil, 'w') do
|
|
341
|
+
d2.shift
|
|
342
|
+
end
|
|
343
|
+
d1.shift
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
assert_equal(File.md5sum(p1.to_s).to_s, p1.basename.to_s, 'bad md5 open')
|
|
347
|
+
assert_equal(1, cache.nb_files, 'bad nb_files')
|
|
348
|
+
assert_equal(p1, cache[0])
|
|
349
|
+
assert_equal(10, cache.size)
|
|
350
|
+
assert_equal(p1, cache.present?(p1.basename))
|
|
351
|
+
|
|
352
|
+
ensure
|
|
353
|
+
repo.rmtree if repo
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
#
|
|
358
|
+
# Utilities
|
|
359
|
+
#
|
|
360
|
+
protected
|
|
361
|
+
def check_atime(cache)
|
|
362
|
+
j = 0
|
|
363
|
+
for i in 1...cache.nb_files do
|
|
364
|
+
assert(cache[j].atime <= cache[i].atime, "bad atime order for #{i}")
|
|
365
|
+
j = i
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
end # class CacheTest
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
end
|
|
373
|
+
|