fluentd 0.14.12-x64-mingw32 → 0.14.13-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +2 -0
  3. data/ChangeLog +33 -0
  4. data/bin/fluent-plugin-config-format +5 -0
  5. data/bin/fluent-plugin-generate +5 -0
  6. data/lib/fluent/command/plugin_config_formatter.rb +258 -0
  7. data/lib/fluent/command/plugin_generator.rb +301 -0
  8. data/lib/fluent/compat/detach_process_mixin.rb +25 -0
  9. data/lib/fluent/compat/filter.rb +1 -1
  10. data/lib/fluent/compat/input.rb +1 -0
  11. data/lib/fluent/compat/output.rb +8 -5
  12. data/lib/fluent/config/configure_proxy.rb +31 -8
  13. data/lib/fluent/configurable.rb +2 -2
  14. data/lib/fluent/output.rb +3 -0
  15. data/lib/fluent/plugin/buffer/file_chunk.rb +52 -11
  16. data/lib/fluent/plugin/filter.rb +1 -1
  17. data/lib/fluent/plugin/filter_record_transformer.rb +5 -1
  18. data/lib/fluent/plugin/in_tail.rb +13 -6
  19. data/lib/fluent/plugin/input.rb +1 -1
  20. data/lib/fluent/plugin/output.rb +26 -6
  21. data/lib/fluent/plugin/parser_apache.rb +1 -1
  22. data/lib/fluent/plugin/parser_apache_error.rb +1 -1
  23. data/lib/fluent/plugin/parser_multiline.rb +1 -0
  24. data/lib/fluent/plugin/parser_nginx.rb +1 -1
  25. data/lib/fluent/plugin/parser_regexp.rb +18 -0
  26. data/lib/fluent/plugin_helper.rb +19 -1
  27. data/lib/fluent/plugin_helper/retry_state.rb +40 -16
  28. data/lib/fluent/process.rb +22 -0
  29. data/lib/fluent/supervisor.rb +2 -21
  30. data/lib/fluent/test/helpers.rb +14 -0
  31. data/lib/fluent/version.rb +1 -1
  32. data/templates/new_gem/Gemfile +3 -0
  33. data/templates/new_gem/README.md.erb +43 -0
  34. data/templates/new_gem/Rakefile +13 -0
  35. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  36. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  37. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  38. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  39. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  40. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  41. data/templates/new_gem/test/helper.rb.erb +8 -0
  42. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  43. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  44. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  45. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  46. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  47. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  48. data/templates/plugin_config_formatter/param.md.erb +34 -0
  49. data/templates/plugin_config_formatter/section.md.erb +12 -0
  50. data/test/command/test_binlog_reader.rb +0 -9
  51. data/test/command/test_plugin_config_formatter.rb +275 -0
  52. data/test/command/test_plugin_generator.rb +66 -0
  53. data/test/config/test_configure_proxy.rb +89 -45
  54. data/test/plugin/data/log/foo/bar2 +0 -0
  55. data/test/plugin/test_in_tail.rb +97 -8
  56. data/test/plugin/test_output.rb +18 -0
  57. data/test/plugin/test_output_as_buffered.rb +35 -0
  58. data/test/plugin_helper/test_compat_parameters.rb +2 -0
  59. data/test/plugin_helper/test_retry_state.rb +23 -0
  60. data/test/test_output.rb +5 -0
  61. data/test/test_process.rb +14 -0
  62. metadata +37 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7276bc37edf4699d8fec6b1e0fd20eb2c4ed178b
4
- data.tar.gz: a1a66a73c39069a7686d2ece78bdb04a4b7b5e89
3
+ metadata.gz: 015a81e0997ba3753d8006d65d769eff8c9be28e
4
+ data.tar.gz: 61e3dd1e6b72eade17e5ce205998cc65b87e765e
5
5
  SHA512:
6
- metadata.gz: dd0c5b32581daf75c8cc7b3f5ad41806a6b6c0a02623be693d2d985014cb4163845ad1286cce9ee6b9ad865b5352e5ffc2265709add94e3e6b7eb7e61eb61ae5
7
- data.tar.gz: e20f11a97ffc6b21d534ac07d054c0ffc22a22beb0ba68702c776d8bb519525eb0d05f26b42b900f613cf5192560f01dec0f55548e56652def41ea230f6a010c
6
+ metadata.gz: 405db88d455d0d85c61ed7524ea8613312510172f2b2607e73b02cd20c742bd49a293745eb4a6f319fd44e5bbb541bec0f83803f688fa13fceefaf40f1afaec4
7
+ data.tar.gz: 284a098b335b94b883fb7b8ce248867412c8a2373605a284141dd502d5aefb9254086f1aa61c37cacc55ec5c1d62ef1ed99dbc21dfd128a5f7effa77e17d5857
@@ -16,6 +16,8 @@ Don't use Github issue for asking questions. Here are examples:
16
16
  We may close such questions to keep clear repository for developers and users.
17
17
  Github issue is mainly for submitting a bug report or feature request. See below.
18
18
 
19
+ If you can't judge your case is a bug or not, use mailing list or slack first.
20
+
19
21
  ## Found a bug?
20
22
 
21
23
  If you find a bug of Fluentd or a mistake in the documentation, you can help us by
data/ChangeLog CHANGED
@@ -1,5 +1,38 @@
1
1
  # v0.14
2
2
 
3
+ ## Release v0.14.13 - 2017/02/17
4
+
5
+ ### New features / Enhancements
6
+
7
+ * in_tail: Add 'limit_recently_modified' to limit watch files.
8
+ https://github.com/fluent/fluentd/pull/1474
9
+ * configuration: Improve 'flush_interval' handling for better message and backward compatibility
10
+ https://github.com/fluent/fluentd/pull/1442
11
+ * command: Add 'fluent-plugin-generate' command
12
+ https://github.com/fluent/fluentd/pull/1427
13
+ * output: Skip record when 'Output#format' returns nil
14
+ https://github.com/fluent/fluentd/pull/1469
15
+
16
+ ### Bug fixes
17
+
18
+ * output: Secondary calculation should consider 'retry_max_times'
19
+ https://github.com/fluent/fluentd/pull/1452
20
+ * Fix regression of deprecatd 'process' module
21
+ https://github.com/fluent/fluentd/pull/1443
22
+ * Fix missing parser_regex require
23
+ https://github.com/fluent/fluentd/issues/1458
24
+ https://github.com/fluent/fluentd/pull/1453
25
+ * Keep 'Fluent::BufferQueueLimitError' for exsting plugins
26
+ https://github.com/fluent/fluentd/pull/1456
27
+ * in_tail: Untracked files should be removed from watching list to avoid memory bloat
28
+ https://github.com/fluent/fluentd/pull/1467
29
+ * in_tail: directories should be skipped when the ** pattern is used
30
+ https://github.com/fluent/fluentd/pull/1464
31
+ * record_transformer: Revert "Use BasicObject for cleanroom" for `enable_ruby` regression.
32
+ https://github.com/fluent/fluentd/pull/1461
33
+ * buf_file: handle "Too many open files" error to keep buffer and metadata pair
34
+ https://github.com/fluent/fluentd/pull/1468
35
+
3
36
  ## Release v0.14.12 - 2017/01/30
4
37
 
5
38
  ### New features / Enhancements
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.join(__dir__, 'lib'))
3
+ require 'fluent/command/plugin_config_formatter'
4
+
5
+ FluentPluginConfigFormatter.new.call
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path(File.join(__dir__, '..', 'lib'))
3
+ require 'fluent/command/plugin_generator'
4
+
5
+ FluentPluginGenerator.new.call
@@ -0,0 +1,258 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require "erb"
18
+ require "optparse"
19
+ require "pathname"
20
+ require "fluent/plugin"
21
+ require "fluent/env"
22
+ require "fluent/engine"
23
+ require "fluent/system_config"
24
+ require "fluent/config/element"
25
+
26
+ class FluentPluginConfigFormatter
27
+
28
+ AVAILABLE_FORMATS = [:markdown, :txt, :json]
29
+ SUPPORTED_TYPES = [
30
+ "input", "output", "filter",
31
+ "buffer", "parser", "formatter", "storage"
32
+ ]
33
+
34
+ def initialize(argv = ARGV)
35
+ @argv = argv
36
+
37
+ @compact = false
38
+ @format = :markdown
39
+ @verbose = false
40
+ @libs = []
41
+ @plugin_dirs = []
42
+ @options = {}
43
+
44
+ prepare_option_parser
45
+ end
46
+
47
+ def call
48
+ parse_options!
49
+ init_libraries
50
+ @plugin = Fluent::Plugin.__send__("new_#{@plugin_type}", @plugin_name)
51
+ dumped_config = {}
52
+ if @plugin.class.respond_to?(:plugin_helpers)
53
+ dumped_config[:plugin_helpers] = @plugin.class.plugin_helpers
54
+ end
55
+ @plugin.class.ancestors.reverse_each do |plugin_class|
56
+ next unless plugin_class.respond_to?(:dump_config_definition)
57
+ unless @verbose
58
+ next if plugin_class.name =~ /::PluginHelper::/
59
+ end
60
+ dumped_config_definition = plugin_class.dump_config_definition
61
+ dumped_config[plugin_class.name] = dumped_config_definition unless dumped_config_definition.empty?
62
+ end
63
+ case @format
64
+ when :txt
65
+ puts dump_txt(dumped_config)
66
+ when :markdown
67
+ puts dump_markdown(dumped_config)
68
+ when :json
69
+ puts dump_json(dumped_config)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def dump_txt(dumped_config)
76
+ dumped = ""
77
+ plugin_helpers = dumped_config.delete(:plugin_helpers)
78
+ if plugin_helpers && !plugin_helpers.empty?
79
+ dumped << "helpers: #{plugin_helpers.join(',')}\n"
80
+ end
81
+ if @verbose
82
+ dumped_config.each do |name, config|
83
+ dumped << "#{name}\n"
84
+ dumped << dump_section_txt(config)
85
+ end
86
+ else
87
+ configs = dumped_config.values
88
+ root_section = configs.shift
89
+ configs.each do |config|
90
+ root_section.update(config)
91
+ end
92
+ dumped << dump_section_txt(root_section)
93
+ end
94
+ dumped
95
+ end
96
+
97
+ def dump_section_txt(base_section, level = 0)
98
+ dumped = ""
99
+ indent = " " * level
100
+ if base_section[:section]
101
+ sections = []
102
+ params = base_section
103
+ else
104
+ sections, params = base_section.partition {|_name, value| value[:section] }
105
+ end
106
+ params.each do |name, config|
107
+ next if name == :section
108
+ dumped << "#{indent}#{name}: #{config[:type]}: (#{config[:default].inspect})"
109
+ dumped << " # #{config[:description]}" if config.key?(:description)
110
+ dumped << "\n"
111
+ end
112
+ sections.each do |section_name, sub_section|
113
+ required = sub_section.delete(:required)
114
+ multi = sub_section.delete(:multi)
115
+ alias_name = sub_section.delete(:alias)
116
+ required_label = required ? "required" : "optional"
117
+ multi_label = multi ? "multiple" : "single"
118
+ alias_label = "alias: #{alias_name}"
119
+ dumped << "#{indent}<#{section_name}>: #{required_label}, #{multi_label}"
120
+ if alias_name
121
+ dumped << ", #{alias_label}\n"
122
+ else
123
+ dumped << "\n"
124
+ end
125
+ sub_section.delete(:section)
126
+ dumped << dump_section_txt(sub_section, level + 1)
127
+ end
128
+ dumped
129
+ end
130
+
131
+ def dump_markdown(dumped_config)
132
+ dumped = ""
133
+ plugin_helpers = dumped_config.delete(:plugin_helpers)
134
+ if plugin_helpers && !plugin_helpers.empty?
135
+ dumped = "## Plugin helpers\n\n"
136
+ plugin_helpers.each do |plugin_helper|
137
+ dumped << "* #{plugin_helper}\n"
138
+ end
139
+ dumped << "\n"
140
+ end
141
+ dumped_config.each do |name, config|
142
+ if name == @plugin.class.name
143
+ dumped << "## #{name}\n\n"
144
+ dumped << dump_section_markdown(config)
145
+ else
146
+ dumped << "* See also: #{name}\n\n"
147
+ end
148
+ end
149
+ dumped
150
+ end
151
+
152
+ def dump_section_markdown(base_section, level = 0)
153
+ dumped = ""
154
+ if base_section[:section]
155
+ sections = []
156
+ params = base_section
157
+ else
158
+ sections, params = base_section.partition {|_name, value| value[:section] }
159
+ end
160
+ params.each do |name, config|
161
+ next if name == :section
162
+ template_name = @compact ? "param.md-compact.erb" : "param.md.erb"
163
+ dumped << ERB.new(template_path(template_name).read, nil, "-").result(binding)
164
+ end
165
+ dumped << "\n"
166
+ sections.each do |section_name, sub_section|
167
+ required = sub_section.delete(:required)
168
+ multi = sub_section.delete(:multi)
169
+ alias_name = sub_section.delete(:alias)
170
+ $log.trace(name: section_name, required: required, multi: multi, alias_name: alias_name)
171
+ sub_section.delete(:section)
172
+ dumped << ERB.new(template_path("section.md.erb").read, nil, "-").result(binding)
173
+ end
174
+ dumped
175
+ end
176
+
177
+ def dump_json(dumped_config)
178
+ if @compact
179
+ JSON.generate(dumped_config)
180
+ else
181
+ JSON.pretty_generate(dumped_config)
182
+ end
183
+ end
184
+
185
+ def usage(message = nil)
186
+ puts @parser.to_s
187
+ puts
188
+ puts "Error: #{message}" if message
189
+ exit(false)
190
+ end
191
+
192
+ def prepare_option_parser
193
+ @parser = OptionParser.new
194
+ @parser.banner = <<BANNER
195
+ Usage: #{$0} [options] <type> <name>
196
+
197
+ Output plugin config definitions
198
+
199
+ Arguments:
200
+ \ttype: #{SUPPORTED_TYPES.join(",")}
201
+ \tname: registered plugin name
202
+
203
+ Options:
204
+ BANNER
205
+ @parser.on("--verbose", "Be verbose") do
206
+ @verbose = true
207
+ end
208
+ @parser.on("-c", "--compact", "Compact output") do
209
+ @compact = true
210
+ end
211
+ @parser.on("-f", "--format=FORMAT", "Specify format. (#{AVAILABLE_FORMATS.join(',')})") do |s|
212
+ format = s.to_sym
213
+ usage("Unsupported format: #{s}") unless AVAILABLE_FORMATS.include?(format)
214
+ @format = format
215
+ end
216
+ @parser.on("-I PATH", "Add PATH to $LOAD_PATH") do |s|
217
+ $LOAD_PATH.unshift(s)
218
+ end
219
+ @parser.on("-r NAME", "Load library") do |s|
220
+ @libs << s
221
+ end
222
+ @parser.on("-p", "--plugin=DIR", "Add plugin directory") do |s|
223
+ @plugin_dirs << s
224
+ end
225
+ end
226
+
227
+ def parse_options!
228
+ @parser.parse!(@argv)
229
+
230
+ raise "Must specify plugin type and name" unless @argv.size == 2
231
+
232
+ @plugin_type, @plugin_name = @argv
233
+ @options = {
234
+ compact: @compact,
235
+ format: @format,
236
+ verbose: @verbose,
237
+ }
238
+ rescue => e
239
+ usage(e)
240
+ end
241
+
242
+ def init_libraries
243
+ @libs.each do |lib|
244
+ require lib
245
+ end
246
+
247
+ @plugin_dirs.each do |dir|
248
+ if Dir.exist?(dir)
249
+ dir = File.expand_path(dir)
250
+ Fluent::Plugin.add_plugin_dir(dir)
251
+ end
252
+ end
253
+ end
254
+
255
+ def template_path(name)
256
+ (Pathname(__dir__) + "../../../templates/plugin_config_formatter/#{name}").realpath
257
+ end
258
+ end
@@ -0,0 +1,301 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require "optparse"
18
+ require "pathname"
19
+ require "fileutils"
20
+ require "erb"
21
+ require "open-uri"
22
+
23
+ require "fluent/registry"
24
+
25
+ class FluentPluginGenerator
26
+ attr_reader :type, :name
27
+ attr_reader :license_name
28
+
29
+ SUPPORTED_TYPES = ["input", "output", "filter", "parser", "formatter"]
30
+
31
+ def initialize(argv = ARGV)
32
+ @argv = argv
33
+ @parser = prepare_parser
34
+
35
+ @license_name = "Apache-2.0"
36
+ @overwrite_all = false
37
+ end
38
+
39
+ def call
40
+ parse_options!
41
+ FileUtils.mkdir_p(gem_name)
42
+ Dir.chdir(gem_name) do
43
+ copy_license
44
+ template_directory.find do |path|
45
+ next if path.directory?
46
+ dest_dir = path.dirname.sub(/\A#{Regexp.quote(template_directory.to_s)}\/?/, "")
47
+ dest_file = dest_filename(path)
48
+ if path.extname == ".erb"
49
+ if path.fnmatch?("*/plugin/*")
50
+ next unless path.basename.fnmatch?("*#{type}*")
51
+ end
52
+ template(path, dest_dir + dest_file)
53
+ else
54
+ file(path, dest_dir + dest_file)
55
+ end
56
+ end
57
+ pid = spawn("git", "init", ".")
58
+ Process.wait(pid)
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def template_directory
65
+ (Pathname(__dir__) + "../../../templates/new_gem").realpath
66
+ end
67
+
68
+ def template_file(filename)
69
+ template_directory + filename
70
+ end
71
+
72
+ def template(source, dest)
73
+ dest.dirname.mkpath
74
+ contents = ERB.new(source.read, nil, "-").result(binding)
75
+ label = create_label(dest, contents)
76
+ puts "\t#{label} #{dest}"
77
+ if label == "conflict"
78
+ return unless overwrite?(dest)
79
+ end
80
+ File.write(dest, contents)
81
+ end
82
+
83
+ def file(source, dest)
84
+ label = create_label(dest, source.read)
85
+ puts "\t#{label} #{dest}"
86
+ if label == "conflict"
87
+ return unless overwrite?(dest)
88
+ end
89
+ FileUtils.cp(source, dest)
90
+ end
91
+
92
+ def prepare_parser
93
+ @parser = OptionParser.new
94
+ @parser.banner = <<BANNER
95
+ Usage: fluent-plugin-generate [options] <type> <name>
96
+
97
+ Generate a project skeleton for creating a Fluentd plugin
98
+
99
+ Arguments:
100
+ \ttype: #{SUPPORTED_TYPES.join(",")}
101
+ \tname: Your plugin name
102
+
103
+ Options:
104
+ BANNER
105
+
106
+ @parser.on("--[no-]license=NAME", "Specify license name (default: Apache-2.0)") do |v|
107
+ @license_name = v || "no-license"
108
+ end
109
+ @parser
110
+ end
111
+
112
+ def parse_options!
113
+ @parser.parse!(@argv)
114
+ unless @argv.size == 2
115
+ raise ArgumentError, "Missing arguments"
116
+ end
117
+ @type, @name = @argv
118
+ rescue => e
119
+ usage("#{e.class}:#{e.message}")
120
+ end
121
+
122
+ def usage(message = "")
123
+ puts message
124
+ puts
125
+ puts @parser.help
126
+ exit(false)
127
+ end
128
+
129
+ def user_name
130
+ v = `git config --get user.name`.chomp
131
+ v.empty? ? "TODO: Write your name" : v
132
+ end
133
+
134
+ def user_email
135
+ v = `git config --get user.email`.chomp
136
+ v.empty? ? "TODO: Write your email" : v
137
+ end
138
+
139
+ def gem_name
140
+ "fluent-plugin-#{name}"
141
+ end
142
+
143
+ def class_name
144
+ "#{name.capitalize}#{type.capitalize}"
145
+ end
146
+
147
+ def plugin_filename
148
+ case type
149
+ when "input"
150
+ "in_#{name}.rb"
151
+ when "output"
152
+ "out_#{name}.rb"
153
+ else
154
+ "#{type}_#{name}.rb"
155
+ end
156
+ end
157
+
158
+ def test_filename
159
+ case type
160
+ when "input"
161
+ "test_in_#{name}.rb"
162
+ when "output"
163
+ "test_out_#{name}.rb"
164
+ else
165
+ "test_#{type}_#{name}.rb"
166
+ end
167
+ end
168
+
169
+ def dest_filename(path)
170
+ case path.to_s
171
+ when %r!\.gemspec!
172
+ "#{gem_name}.gemspec"
173
+ when %r!lib/fluent/plugin!
174
+ plugin_filename
175
+ when %r!test/plugin!
176
+ test_filename
177
+ else
178
+ path.basename.sub_ext("")
179
+ end
180
+ end
181
+
182
+ def preamble
183
+ @license.preamble(user_name)
184
+ end
185
+
186
+ def copy_license
187
+ # in gem_name directory
188
+ return unless license_name
189
+ puts "License: #{license_name}"
190
+ license_class = self.class.lookup_license(license_name)
191
+ @license = license_class.new
192
+ Pathname("LICENSE").write(@license.text) unless @license.text.empty?
193
+ rescue Fluent::ConfigError
194
+ usage("Unknown license: #{license_name}")
195
+ rescue => ex
196
+ usage("#{ex.class}: #{ex.message}")
197
+ end
198
+
199
+ def create_label(dest, contents)
200
+ if dest.exist?
201
+ if dest.read == contents
202
+ "identical"
203
+ else
204
+ "conflict"
205
+ end
206
+ else
207
+ "create"
208
+ end
209
+ end
210
+
211
+ def overwrite?(dest)
212
+ return true if @overwrite_all
213
+ loop do
214
+ print "Overwrite #{dest}? (enter \"h\" for help) [Ynaqh]"
215
+ answer = $stdin.gets.chomp
216
+ return true if /\Ay\z/i =~ answer || answer.empty?
217
+ case answer
218
+ when "n"
219
+ return false
220
+ when "a"
221
+ @overwrite_all = true
222
+ return true
223
+ when "q"
224
+ exit
225
+ when "h"
226
+ puts <<HELP
227
+ \tY - yes, overwrite
228
+ \tn - no, do not overwrite
229
+ \ta - all, overwrite this and all others
230
+ \tq - quite, abort
231
+ \th - help, show this help
232
+ HELP
233
+ end
234
+ puts "Retrying..."
235
+ end
236
+ end
237
+
238
+ class NoLicense
239
+ attr_reader :name, :full_name, :text
240
+
241
+ def initialize
242
+ @name = ""
243
+ @full_name = ""
244
+ @text = ""
245
+ end
246
+
247
+ def preamble(usename)
248
+ ""
249
+ end
250
+ end
251
+
252
+ class ApacheLicense
253
+ LICENSE_URL = "http://www.apache.org/licenses/LICENSE-2.0.txt"
254
+
255
+ attr_reader :text
256
+
257
+ def initialize
258
+ @text = ""
259
+ @preamble_source = ""
260
+ @preamble = nil
261
+ open(LICENSE_URL) do |io|
262
+ @text = io.read
263
+ end
264
+ @preamble_source = @text[/^(\s*Copyright.+)/m, 1]
265
+ end
266
+
267
+ def name
268
+ "Apache-2.0"
269
+ end
270
+
271
+ def full_name
272
+ "Apache License, Version 2.0"
273
+ end
274
+
275
+ def preamble(user_name)
276
+ @preamble ||= @preamble_source.dup.tap do |source|
277
+ source.gsub!(/\[yyyy\]/, "#{Date.today.year}-")
278
+ source.gsub!(/\[name of copyright owner\]/, user_name)
279
+ source.gsub!(/^ {2}|^$/, "#")
280
+ source.chomp!
281
+ end
282
+ end
283
+ end
284
+
285
+ LICENSE_REGISTRY = Fluent::Registry.new(:license, "")
286
+
287
+ def self.register_license(license, klass)
288
+ LICENSE_REGISTRY.register(license, klass)
289
+ end
290
+
291
+ def self.lookup_license(license)
292
+ LICENSE_REGISTRY.lookup(license)
293
+ end
294
+
295
+ {
296
+ "no-license" => NoLicense,
297
+ "Apache-2.0" => ApacheLicense
298
+ }.each do |license, klass|
299
+ register_license(license, klass)
300
+ end
301
+ end