vcs 0.1 → 0.2.148

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. data/Rakefile +17 -3
  2. data/bin/vcs +57 -34
  3. data/doc/jamis.rb +564 -0
  4. data/ruby_ex/abstract.rb +254 -0
  5. data/ruby_ex/abstract_node.rb +85 -0
  6. data/ruby_ex/algorithms/simulated_annealing.rb +140 -0
  7. data/ruby_ex/array_each_pair.rb +18 -0
  8. data/ruby_ex/ask.rb +101 -0
  9. data/ruby_ex/attributed_class.rb +302 -0
  10. data/ruby_ex/cache.rb +373 -0
  11. data/ruby_ex/checkout.rb +12 -0
  12. data/ruby_ex/choose.rb +271 -0
  13. data/ruby_ex/commands.rb +18 -0
  14. data/ruby_ex/commands/command.rb +401 -0
  15. data/ruby_ex/commands/datas.rb +16 -0
  16. data/ruby_ex/commands/datas/data.rb +33 -0
  17. data/ruby_ex/commands/datas/factory.rb +66 -0
  18. data/ruby_ex/commands/factory.rb +66 -0
  19. data/ruby_ex/commands/helpers.rb +67 -0
  20. data/ruby_ex/commands/pipe.rb +64 -0
  21. data/ruby_ex/commands/runners.rb +17 -0
  22. data/ruby_ex/commands/runners/exec.rb +49 -0
  23. data/ruby_ex/commands/runners/fork.rb +97 -0
  24. data/ruby_ex/commands/runners/runner.rb +107 -0
  25. data/ruby_ex/commands/seq.rb +27 -0
  26. data/ruby_ex/config_file.rb +96 -0
  27. data/ruby_ex/const_regexp.rb +59 -0
  28. data/ruby_ex/daemon.rb +134 -0
  29. data/ruby_ex/diff.rb +667 -0
  30. data/ruby_ex/dlogger.rb +62 -0
  31. data/ruby_ex/drb/dispatcher.rb +252 -0
  32. data/ruby_ex/drb/dispatcher_server_test.rb +29 -0
  33. data/ruby_ex/drb/drb_observable.rb +97 -0
  34. data/ruby_ex/drb/drb_observable_pool.rb +27 -0
  35. data/ruby_ex/drb/drb_service.rb +43 -0
  36. data/ruby_ex/drb/drb_undumped_attributes.rb +55 -0
  37. data/ruby_ex/drb/drb_undumped_indexed_object.rb +54 -0
  38. data/ruby_ex/drb/insecure_protected_methods.rb +103 -0
  39. data/ruby_ex/drb/session_client_test.rb +40 -0
  40. data/ruby_ex/drb/session_manager.rb +246 -0
  41. data/ruby_ex/drb/session_server.rb +53 -0
  42. data/ruby_ex/dtime.rb +143 -0
  43. data/ruby_ex/dumpable_proc.rb +63 -0
  44. data/ruby_ex/exception.rb +32 -0
  45. data/ruby_ex/filetype.rb +229 -0
  46. data/ruby_ex/fileutils_ex.rb +44 -0
  47. data/ruby_ex/fold.rb +58 -0
  48. data/ruby_ex/generate_id.rb +44 -0
  49. data/ruby_ex/hookable.rb +262 -0
  50. data/ruby_ex/hooker.rb +54 -0
  51. data/ruby_ex/inactive_timeout.rb +137 -0
  52. data/ruby_ex/indexed_node.rb +66 -0
  53. data/ruby_ex/io_marshal.rb +100 -0
  54. data/ruby_ex/ioo.rb +194 -0
  55. data/ruby_ex/labeled_node.rb +63 -0
  56. data/ruby_ex/logger_observer.rb +23 -0
  57. data/ruby_ex/md5sum.rb +66 -0
  58. data/ruby_ex/mktemp.rb +208 -0
  59. data/ruby_ex/module/attr_once.rb +36 -0
  60. data/ruby_ex/module/autoload_tree.rb +75 -0
  61. data/ruby_ex/module/hierarchy.rb +335 -0
  62. data/ruby_ex/module/instance_method_visibility.rb +73 -0
  63. data/ruby_ex/module_ex.rb +11 -0
  64. data/ruby_ex/node.rb +80 -0
  65. data/ruby_ex/object_monitor.rb +145 -0
  66. data/ruby_ex/object_monitor_activity.rb +33 -0
  67. data/ruby_ex/observable.rb +140 -0
  68. data/ruby_ex/observable_pool.rb +293 -0
  69. data/ruby_ex/orderedhash.rb +252 -0
  70. data/ruby_ex/pathname_ex.rb +152 -0
  71. data/ruby_ex/pp_hierarchy.rb +29 -0
  72. data/ruby_ex/pseudo_cache.rb +190 -0
  73. data/ruby_ex/queue.rb +56 -0
  74. data/ruby_ex/random_generators.rb +25 -0
  75. data/ruby_ex/random_generators/random_generator.rb +31 -0
  76. data/ruby_ex/random_generators/ruby.rb +23 -0
  77. data/ruby_ex/safe_eval.rb +348 -0
  78. data/ruby_ex/sendmail.rb +215 -0
  79. data/ruby_ex/service_manager.rb +121 -0
  80. data/ruby_ex/session/administrable.rb +120 -0
  81. data/ruby_ex/session/client.rb +153 -0
  82. data/ruby_ex/session/const.rb +18 -0
  83. data/ruby_ex/session/dispatcher.rb +184 -0
  84. data/ruby_ex/session/error.rb +21 -0
  85. data/ruby_ex/session/fetchable.rb +57 -0
  86. data/ruby_ex/session/fetcher.rb +62 -0
  87. data/ruby_ex/session/hookable.rb +26 -0
  88. data/ruby_ex/session/profile.rb +110 -0
  89. data/ruby_ex/session/server.rb +582 -0
  90. data/ruby_ex/session/test/administrable_test.rb +337 -0
  91. data/ruby_ex/session/test/basic_test.rb +523 -0
  92. data/ruby_ex/session/test/dispatcher_test.rb +409 -0
  93. data/ruby_ex/session/test/fetchable_test.rb +119 -0
  94. data/ruby_ex/session/test/sub_server_test.rb +188 -0
  95. data/ruby_ex/shuffle.rb +30 -0
  96. data/ruby_ex/spring.rb +136 -0
  97. data/ruby_ex/spring_set.rb +137 -0
  98. data/ruby_ex/string_ex.rb +28 -0
  99. data/ruby_ex/symtbl.rb +106 -0
  100. data/ruby_ex/synflow.rb +474 -0
  101. data/ruby_ex/test/unit/ui/yaml/testrunner.rb +164 -0
  102. data/ruby_ex/thread_mutex.rb +10 -0
  103. data/ruby_ex/timeout_ex.rb +81 -0
  104. data/ruby_ex/top_down.rb +73 -0
  105. data/ruby_ex/trace.rb +26 -0
  106. data/ruby_ex/uri/druby.rb +81 -0
  107. data/ruby_ex/uri/file.rb +65 -0
  108. data/ruby_ex/uri/ftp_ex.rb +37 -0
  109. data/ruby_ex/uri/http_ex.rb +43 -0
  110. data/ruby_ex/uri/ssh.rb +92 -0
  111. data/ruby_ex/uri/svn.rb +118 -0
  112. data/ruby_ex/uri_ex.rb +45 -0
  113. data/ruby_ex/verbose_object.rb +30 -0
  114. data/ruby_ex/version.rb +66 -0
  115. data/ruby_ex/yaml/basenode_ext.rb +63 -0
  116. data/ruby_ex/yaml/chop_header.rb +23 -0
  117. data/ruby_ex/yaml/transform.rb +449 -0
  118. data/ruby_ex/yaml/yregexpath.rb +76 -0
  119. data/src/changelog.rb +28 -18
  120. data/src/conflict.rb +20 -0
  121. data/src/diff.rb +18 -0
  122. data/src/diffstat.rb +9 -3
  123. data/src/last_changed_date.rb +18 -0
  124. data/src/mail.rb +33 -65
  125. data/src/message.rb +15 -9
  126. data/src/mycommit.rb +29 -14
  127. data/src/news.rb +24 -3
  128. data/src/status.rb +17 -0
  129. data/src/svn.rb +2 -2
  130. data/src/vcs.rb +24 -3
  131. metadata +124 -5
  132. 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
+
@@ -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
@@ -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
+