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,53 @@
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: session_server.rb 91 2005-01-09 22:24:40Z polrop $
7
+
8
+
9
+ require 'drb/extserv'
10
+
11
+
12
+ module DRb
13
+
14
+ class SessionServer < ExtServ
15
+
16
+ attr_reader :name
17
+
18
+ alias sid name
19
+
20
+ end # class SessionServer
21
+
22
+ end # module DRb
23
+
24
+
25
+ if __FILE__ == $0
26
+
27
+ require 'drb/drb'
28
+ require 'drb/extserv'
29
+ require 'yaml'
30
+ require 'tempfile'
31
+
32
+ class Foo
33
+
34
+ def hello
35
+ 'hello'
36
+ end
37
+
38
+ def salut
39
+ 'salut'
40
+ end
41
+
42
+ end # class SessionManagerTest
43
+
44
+
45
+ raise "usage: #{$0} <uri> <name>" unless ARGV.size == 2
46
+ uri = ARGV.shift
47
+ name = ARGV.shift
48
+
49
+ server = DRb::DRbServer.new("druby://localhost:0", Foo.new, {})
50
+ ext_srv = DRb::SessionServer.new(uri, name, server)
51
+ server.thread.join
52
+
53
+ end
@@ -0,0 +1,143 @@
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: dtime.rb 146 2005-02-01 11:02:42Z polrop $
7
+
8
+
9
+ require 'module/attr_once'
10
+
11
+
12
+ class DTime
13
+
14
+ def initialize(delta)
15
+ @delta = delta.abs
16
+ @min, @sec = @delta.divmod(60)
17
+ @hour, @min = @min.floor.divmod(60)
18
+ @day, @hour = @hour.divmod(24)
19
+ end
20
+
21
+ attr_reader :delta, :sec, :min, :hour, :day
22
+
23
+ def to_s
24
+ @delta.to_s.freeze
25
+ end
26
+
27
+ def inspect
28
+ "#@day days #@hour hours #@min mins #@sec secs".freeze
29
+ end
30
+
31
+ def to_a
32
+ [ @day, @hour, @min, @sec ].freeze
33
+ end
34
+
35
+ def to_i
36
+ @delta.to_i
37
+ end
38
+
39
+ alias to_f delta
40
+
41
+ def hash
42
+ @delta.hash
43
+ end
44
+
45
+ def to_hash
46
+ { :days => @day, :hours => @hour, :mins => @min, :secs => @sec }.freeze
47
+ end
48
+
49
+ def floor
50
+ @delta.floor
51
+ end
52
+
53
+ def round
54
+ @delta.round
55
+ end
56
+
57
+ def to_yaml(opts={})
58
+ ((@day != 0 ? "#@day days " : '') +
59
+ (@hour != 0 ? "#@hour hours " : '') +
60
+ (@min != 0 ? "#@min mins " : '') +
61
+ "#@sec secs").to_yaml(opts)
62
+ end
63
+
64
+ attr_once :to_s, :inspect, :to_a, :to_i, :hash, :to_hash, :floor,
65
+ :round, :to_yaml
66
+
67
+ end # class DTime
68
+
69
+
70
+ if defined? TEST_MODE or __FILE__ == $0
71
+
72
+
73
+ require 'test/unit/ui/yaml/testrunner'
74
+ require 'yaml'
75
+
76
+
77
+ class DTimeTest < Test::Unit::TestCase
78
+
79
+ def test_simple
80
+ d = DTime.new(260.33)
81
+ assert_equal(260, d.delta.floor)
82
+ assert_equal(20, d.sec.floor)
83
+ assert_equal(4, d.min)
84
+ assert_equal(0, d.hour)
85
+ assert_equal(0, d.day)
86
+ end
87
+
88
+ def test_complex
89
+ d = DTime.new(265678.42000)
90
+ assert_equal(265678, d.delta.floor)
91
+ assert_equal(58, d.sec.floor)
92
+ assert_equal(47, d.min)
93
+ assert_equal(1, d.hour)
94
+ assert_equal(3, d.day)
95
+ assert_equal([ 3, 1, 47 ], d.to_a[0..2])
96
+ assert_equal(58, d.to_a[3].floor)
97
+ h = d.to_hash
98
+ assert_equal(3, h[:days])
99
+ assert_equal(1, h[:hours])
100
+ assert_equal(47, h[:mins])
101
+ assert_equal(58, h[:secs].floor)
102
+ end
103
+
104
+ def test_conversion
105
+ d = DTime.new(260.75)
106
+ assert_equal('260.75', d.to_s)
107
+ assert_equal('0 days 0 hours 4 mins 20.75 secs', d.inspect)
108
+ assert_equal(260, d.to_f.floor)
109
+ assert_equal(260, d.to_i)
110
+ assert_equal(260, d.floor)
111
+ assert_equal(261, d.round)
112
+ end
113
+
114
+ def test_negative
115
+ d = DTime.new(-265678.42000)
116
+ assert_equal(265678, d.delta.floor)
117
+ assert_equal(58, d.sec.floor)
118
+ assert_equal(47, d.min)
119
+ assert_equal(1, d.hour)
120
+ assert_equal(3, d.day)
121
+ end
122
+
123
+ def test_marshal
124
+ d = DTime.new(-265678.42000)
125
+ assert_equal(d.floor, Marshal.load(Marshal.dump(d)).floor)
126
+ end
127
+
128
+ def test_to_yaml
129
+ d = DTime.new(265678.42000)
130
+ assert_equal('--- 3 days 1 hours 47 mins 58.4199999999837 secs', d.to_yaml)
131
+ d = DTime.new(5678.42000)
132
+ assert_equal('--- 1 hours 34 mins 38.4200000000001 secs', d.to_yaml)
133
+ d = DTime.new(158.42000)
134
+ assert_equal('--- 2 mins 38.42 secs', d.to_yaml)
135
+ d = DTime.new(58.42000)
136
+ assert_equal('--- 58.42 secs', d.to_yaml)
137
+ end
138
+
139
+ end # class DTimeTest
140
+
141
+
142
+ end
143
+
@@ -0,0 +1,63 @@
1
+ # Copyright: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: dumpable_proc.rb 190 2005-04-04 14:46:40Z ertai $
7
+
8
+ require 'yaml'
9
+ require 'delegate'
10
+
11
+ class DumpableProc < DelegateClass(Proc)
12
+
13
+ def initialize ( str )
14
+ @str = str.freeze
15
+ super(eval("proc { #@str }"))
16
+ end
17
+
18
+ def _dump ( depth )
19
+ Marshal.dump([self.class, @str])
20
+ end
21
+
22
+ def self._load ( data )
23
+ klass, str = Marshal.load(data)
24
+ klass.new(str)
25
+ end
26
+
27
+ def to_yaml ( opts={} )
28
+ "#{to_yaml_type} #@str"
29
+ end
30
+
31
+ def to_s
32
+ @str
33
+ end
34
+
35
+ def to_yaml_type
36
+ '!proc'
37
+ end
38
+
39
+ end
40
+
41
+ YAML.add_builtin_type('!proc') do |type, val|
42
+ DumpableProc.new(val.to_s)
43
+ end
44
+
45
+ if defined? TEST_MODE or $0 == __FILE__
46
+
47
+ require 'test/unit'
48
+
49
+ class DumpableProcTest < Test::Unit::TestCase
50
+
51
+ def test_simple
52
+ p = DumpableProc.new('|x| 3 + x')
53
+ assert_equal(7, p[4])
54
+ str = nil
55
+ assert_nothing_raised { str = Marshal.dump(p) }
56
+ p2 = nil
57
+ assert_nothing_raised { p2 = Marshal.load(str) }
58
+ assert_equal(9, p2[6])
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,32 @@
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
+ # Revision:: $Id: exception.rb 200 2005-04-22 22:55:31Z ertai $
5
+
6
+
7
+ class Exception
8
+
9
+ #FIXME: write a ruby_pp which print exception backtrace exactly as ruby does
10
+ # by cuting long and repetitive backtrace.
11
+
12
+ def long_pp
13
+ str = short_pp + "\n"
14
+ backtrace[1..-1].each { |x| str += " from #{x}\n" } if backtrace
15
+ str.chomp!
16
+ str
17
+ end
18
+
19
+ def short_pp
20
+ if backtrace.nil?
21
+ tiny_pp
22
+ else
23
+ "#{backtrace[0]}: #{tiny_pp}"
24
+ end
25
+ end
26
+
27
+ def tiny_pp
28
+ exc_name = inspect.sub(/^#<(\w+):.+$/, '\1')
29
+ "#{self} (#{exc_name})"
30
+ end
31
+
32
+ end # class Exception
@@ -0,0 +1,229 @@
1
+ require 'pathname_ex'
2
+ require 'abstract'
3
+
4
+ module FileType
5
+
6
+ @@subclasses = []
7
+
8
+ def self.register ( klass )
9
+ @@subclasses << klass
10
+ end
11
+
12
+ class Generic
13
+ include Abstract
14
+
15
+ attr_reader :path, :base, :ext
16
+
17
+ def initialize ( path )
18
+ @path = Pathname.new(path)
19
+ re = self.class.extension
20
+ raise ArgumentError, "bad class #{self.class}" if re.nil?
21
+ unless @path.to_s =~ re
22
+ raise ArgumentError, "#{@path} do not match /#{re.source}/"
23
+ end
24
+ @base, @ext = $`, $&
25
+ if @ext.empty?
26
+ @base = self
27
+ else
28
+ @base = FileType.guess(@base)
29
+ end
30
+ end
31
+
32
+ def extsplit
33
+ [@base, @ext]
34
+ end
35
+
36
+ def self.inherited ( klass )
37
+ FileType.register(klass)
38
+ end
39
+
40
+ def to_s
41
+ @path.to_s
42
+ end
43
+
44
+ def self.extension
45
+ const_get(:EXTENSION)
46
+ end
47
+
48
+ def self.match_type ( path, max, best )
49
+ ext_re = self.extension
50
+ if path.to_s =~ ext_re and $&.size > max
51
+ return [$&.size, self]
52
+ end
53
+ return [max, best]
54
+ end
55
+
56
+ def + ( arg )
57
+ @path + arg.to_s
58
+ end
59
+
60
+ end # class Generic
61
+
62
+ class Unknown < Generic
63
+ include Concrete
64
+ EXTENSION = /(\.[^.\/]+)?$/
65
+ end # class Unknown
66
+
67
+ class ExtractError < Exception
68
+ end
69
+
70
+ module Extractable
71
+
72
+ def self.included ( aClass )
73
+
74
+ aClass.module_eval do
75
+
76
+ def mk_cmd
77
+ base, ext = @base.extsplit
78
+ @tmp = TempPath.new(base.path.basename, ext)
79
+ cmd = self.class.const_get(:EXTRACT_COMMAND).gsub(/%I/, @path.to_s)
80
+ cmd.gsub!(/%O/, @tmp.to_s)
81
+ cmd
82
+ end
83
+
84
+ def extract
85
+ cmd = mk_cmd
86
+ unless system(cmd)
87
+ @tmp.rmtree if @tmp.exist?
88
+ err = "
89
+ |Cannot extract a file:
90
+ | path: #{to_s}
91
+ | type: #{self.class}
92
+ | command: #{cmd}
93
+ | exit status: #{$? >> 8}
94
+ "
95
+ raise ExtractError, err.gsub(/^\s*|/, '')
96
+ end
97
+ FileType.guess(@tmp)
98
+ end
99
+
100
+ alias :default :extract
101
+
102
+ end
103
+
104
+ end
105
+
106
+ end # module Extractable
107
+
108
+ # FIXME Use the zlib if gzip not available.
109
+ class Gz < Generic
110
+ include Extractable
111
+ include Concrete
112
+ EXTENSION = /(\.(gz|z|Z)|-gz|-z|_z)$/
113
+ EXTRACT_COMMAND = 'gzip -d -c "%I" > "%O"'
114
+ end # class Gz
115
+
116
+ class Bz2 < Generic
117
+ include Extractable
118
+ include Concrete
119
+ EXTENSION = /\.(bz2|bz)$/
120
+ EXTRACT_COMMAND = 'bzip2 -d -c "%I" > "%O"'
121
+ end # class Bz2
122
+
123
+ class Zip < Generic
124
+ include Extractable
125
+ include Concrete
126
+ EXTENSION = /\.zip$/
127
+ EXTRACT_COMMAND = 'unzip -p "%I" > "%O"'
128
+ end # class Zip
129
+
130
+ module ExtractableDir
131
+
132
+ def self.included ( aClass )
133
+
134
+ aClass.module_eval do
135
+
136
+ include Extractable
137
+
138
+ alias :mk_cmd_extractable :mk_cmd
139
+
140
+ def mk_cmd
141
+ cmd = mk_cmd_extractable
142
+ @tmp.mkpath
143
+ @log = TempPath.new
144
+ cmd.gsub!(/%L/, @log.to_s)
145
+ end
146
+
147
+ alias :extract_extractable :extract
148
+
149
+ def extract
150
+ dir = extract_extractable
151
+ # We want the longest common path
152
+ longest = nil
153
+ @log.each_line do |line|
154
+ path = line.split(/\//)
155
+ longest = path if longest.nil?
156
+ longest &= path
157
+ raise CorruptedTarball, self if longest.empty?
158
+ end
159
+ dir.path + longest.join('/')
160
+ end
161
+
162
+ end
163
+
164
+ end
165
+
166
+ end # module ExtractableDir
167
+
168
+ class Tar < Generic
169
+ include ExtractableDir
170
+ include Concrete
171
+ EXTENSION = /\.tar$/
172
+ EXTRACT_COMMAND = 'tar xvf "%I" -C "%O" > "%L"'
173
+ end # class Tar
174
+
175
+ class TarGz < Generic
176
+ include ExtractableDir
177
+ include Concrete
178
+ EXTENSION = /\.(tar\.gz|tgz)$/
179
+ EXTRACT_COMMAND = 'tar xvzf "%I" -C "%O" > %L'
180
+ end # class TarGz
181
+
182
+ class TarBz2 < Generic
183
+ include ExtractableDir
184
+ include Concrete
185
+ EXTENSION = /\.(tar\.bz2|tbz2|tbz)$/
186
+ EXTRACT_COMMAND = 'bzip2 -c -d %I | tar xvf - -C "%O" > "%L"'
187
+ end # class TarBz2
188
+
189
+ class Directory < Generic
190
+ include Concrete
191
+
192
+ EXTENSION = /()$/
193
+
194
+ def self.match_type ( path, max, best )
195
+ if File.directory? path
196
+ return [path.to_s.size, self]
197
+ end
198
+ return [max, best]
199
+ end
200
+
201
+ end # class Directory
202
+
203
+ def self.guess_class ( path )
204
+ lazy_init
205
+ max = -1
206
+ best = Unknown
207
+ @@subclasses.each do |klass|
208
+ max, best = klass.match_type(path, max, best)
209
+ end
210
+ return best
211
+ end
212
+
213
+ def self.guess ( path )
214
+ guess_class(path).new(path)
215
+ end
216
+
217
+ def self.lazy_init
218
+ return if defined? @@init
219
+ @@init = true
220
+ @@subclasses.delete_if do |klass|
221
+ klass.abstract? or not (klass.is_a? Class) # and constants.include? klass)
222
+ end
223
+ @@subclasses.each do |klass|
224
+ ext = klass.extension
225
+ raise ArgumentError, "Bad extension #{ext}" unless ext.is_a? Regexp
226
+ end
227
+ end
228
+
229
+ end # module FileType