nucleon 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Gemfile +4 -8
  2. data/Gemfile.lock +0 -28
  3. data/README.rdoc +13 -5
  4. data/Rakefile +9 -1
  5. data/VERSION +1 -1
  6. data/bin/nucleon +55 -0
  7. data/lib/core/codes.rb +107 -0
  8. data/lib/core/config/collection.rb +57 -0
  9. data/lib/core/config/options.rb +70 -0
  10. data/lib/core/config.rb +342 -0
  11. data/lib/core/core.rb +54 -0
  12. data/lib/core/errors.rb +84 -0
  13. data/lib/core/facade.rb +283 -0
  14. data/lib/core/gems.rb +80 -0
  15. data/lib/core/manager.rb +594 -0
  16. data/lib/core/mixin/action/commit.rb +58 -0
  17. data/lib/core/mixin/action/project.rb +53 -0
  18. data/lib/core/mixin/action/push.rb +52 -0
  19. data/lib/core/mixin/config/collection.rb +53 -0
  20. data/lib/core/mixin/config/options.rb +39 -0
  21. data/lib/core/mixin/macro/object_interface.rb +361 -0
  22. data/lib/core/mixin/macro/plugin_interface.rb +380 -0
  23. data/lib/core/mixin/settings.rb +46 -0
  24. data/lib/core/mixin/sub_config.rb +148 -0
  25. data/lib/core/mod/hash.rb +29 -0
  26. data/lib/core/plugin/action.rb +371 -0
  27. data/lib/core/plugin/base.rb +313 -0
  28. data/lib/core/plugin/command.rb +98 -0
  29. data/lib/core/plugin/event.rb +53 -0
  30. data/lib/core/plugin/extension.rb +12 -0
  31. data/lib/core/plugin/project.rb +890 -0
  32. data/lib/core/plugin/template.rb +80 -0
  33. data/lib/core/plugin/translator.rb +38 -0
  34. data/lib/core/util/cli.rb +353 -0
  35. data/lib/core/util/console.rb +237 -0
  36. data/lib/core/util/data.rb +404 -0
  37. data/lib/core/util/disk.rb +114 -0
  38. data/lib/core/util/git.rb +43 -0
  39. data/lib/core/util/liquid.rb +17 -0
  40. data/lib/core/util/logger.rb +147 -0
  41. data/lib/core/util/package.rb +93 -0
  42. data/lib/core/util/shell.rb +239 -0
  43. data/lib/nucleon/action/add.rb +69 -0
  44. data/lib/nucleon/action/create.rb +52 -0
  45. data/lib/nucleon/action/extract.rb +49 -0
  46. data/lib/nucleon/action/remove.rb +51 -0
  47. data/lib/nucleon/action/save.rb +53 -0
  48. data/lib/nucleon/action/update.rb +37 -0
  49. data/lib/nucleon/command/bash.rb +146 -0
  50. data/lib/nucleon/event/regex.rb +52 -0
  51. data/lib/nucleon/project/git.rb +465 -0
  52. data/lib/nucleon/project/github.rb +108 -0
  53. data/lib/nucleon/template/json.rb +16 -0
  54. data/lib/nucleon/template/wrapper.rb +16 -0
  55. data/lib/nucleon/template/yaml.rb +16 -0
  56. data/lib/nucleon/translator/json.rb +27 -0
  57. data/lib/nucleon/translator/yaml.rb +27 -0
  58. data/lib/nucleon.rb +18 -15
  59. data/locales/en.yml +3 -132
  60. data/nucleon.gemspec +66 -27
  61. data/spec/core/util/console_spec.rb +489 -0
  62. metadata +109 -96
@@ -0,0 +1,80 @@
1
+
2
+ module Nucleon
3
+ module Plugin
4
+ class Template < Base
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Template plugin interface
8
+
9
+
10
+ #-----------------------------------------------------------------------------
11
+ # Property accessor / modifiers
12
+
13
+ #-----------------------------------------------------------------------------
14
+ # Operations
15
+
16
+ def process(value)
17
+ case value
18
+ when String, Symbol
19
+ return nil if Util::Data.undef?(value)
20
+ return 'false' if value == false
21
+ return 'true' if value == true
22
+ return value.to_s if value.is_a?(Symbol)
23
+
24
+ when Hash
25
+ results = {}
26
+ value.each do |key, item|
27
+ result = process(item)
28
+ unless result.nil?
29
+ results[key] = result
30
+ end
31
+ value = results
32
+ end
33
+
34
+ when Array
35
+ results = []
36
+ value.each_with_index do |item, index|
37
+ result = process(item)
38
+ unless result.nil?
39
+ results << result
40
+ end
41
+ end
42
+ value = results
43
+ end
44
+ return value
45
+ end
46
+
47
+ #---
48
+
49
+ def render(data)
50
+ normalize = get(:normalize_template, true)
51
+ interpolate = get(:interpolate_template, true)
52
+
53
+ logger.debug("Rendering data: normalize: #{normalize.inspect}; interpolate: #{interpolate.inspect}: #{data.inspect}")
54
+
55
+ if normalize
56
+ data = Config.normalize(data, nil, config)
57
+ logger.debug("Pre-rendering data normalization: #{data.inspect}")
58
+ end
59
+
60
+ if normalize && interpolate
61
+ data = Util::Data.interpolate(data, data, export)
62
+ logger.debug("Pre-rendering data interpolation: #{data.inspect}")
63
+ end
64
+ return render_processed(process(data))
65
+ end
66
+
67
+ #---
68
+
69
+ def render_processed(data)
70
+ logger.debug("Rendering #{plugin_provider} data: #{data.inspect}")
71
+
72
+ output = ''
73
+ output = yield(output) if block_given?
74
+
75
+ logger.debug("Completed rendering of #{plugin_provider} data: #{output}")
76
+ return output
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,38 @@
1
+
2
+ module Nucleon
3
+ module Plugin
4
+ class Translator < Base
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Translator plugin interface
8
+
9
+ #-----------------------------------------------------------------------------
10
+ # Property accessor / modifiers
11
+
12
+ #-----------------------------------------------------------------------------
13
+ # Operations
14
+
15
+ def parse(raw)
16
+ logger.debug("Parsing raw data: #{raw}")
17
+
18
+ properties = {}
19
+ properties = yield(properties) if block_given?
20
+
21
+ logger.debug("Completed parsing data: #{properties.inspect}")
22
+ return properties
23
+ end
24
+
25
+ #---
26
+
27
+ def generate(properties)
28
+ logger.debug("Generating output data: #{properties.inspect}")
29
+
30
+ output = ''
31
+ output = yield(output) if block_given?
32
+
33
+ logger.debug("Completed generating data: #{output}")
34
+ return output
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,353 @@
1
+
2
+ module Nucleon
3
+ module Util
4
+ module CLI
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Utilities
8
+
9
+ def self.message(name, default = nil)
10
+ if default.nil?
11
+ default = :none
12
+ end
13
+ return I18n.t(name.to_s, :default_value => default.to_s)
14
+ end
15
+
16
+ #---
17
+
18
+ def self.encode(data)
19
+ Base64.encode64(Util::Data.to_json(data, false))
20
+ end
21
+
22
+ def self.decode(encoded_string)
23
+ Util::Data.symbol_map(Util::Data.parse_json(Base64.decode64(encoded_string)))
24
+ end
25
+
26
+ #-------------------------------------------------------------------------
27
+ # Parser
28
+
29
+ class Parser
30
+
31
+ attr_accessor :parser
32
+ attr_accessor :options
33
+ attr_accessor :arguments
34
+ attr_accessor :processed
35
+
36
+ #---
37
+
38
+ def initialize(args, banner = '', help = '')
39
+
40
+ @parser = OptionParser.new
41
+
42
+ self.options = {}
43
+ self.arguments = {}
44
+ self.processed = false
45
+
46
+ @arg_settings = []
47
+
48
+ self.banner = banner
49
+ self.help = help
50
+
51
+ yield(self) if block_given?
52
+
53
+ parse_command(args)
54
+ end
55
+
56
+ #---
57
+
58
+ def self.split(args, banner, separator = '')
59
+ main_args = nil
60
+ sub_command = nil
61
+ sub_args = []
62
+
63
+ args.each_index do |index|
64
+ if !args[index].start_with?('-')
65
+ main_args = args[0, index]
66
+ sub_command = args[index]
67
+ sub_args = args[index + 1, args.length - index + 1]
68
+ break
69
+ end
70
+ end
71
+
72
+ main_args = args.dup if main_args.nil?
73
+ results = [ Parser.new(main_args, banner, separator) ]
74
+
75
+ if sub_command
76
+ results << [ sub_command, sub_args ]
77
+ end
78
+
79
+ return results.flatten
80
+ end
81
+
82
+ #---
83
+
84
+ def banner=banner
85
+ parser.banner = banner
86
+ end
87
+
88
+ #---
89
+
90
+ def help
91
+ return parser.help
92
+ end
93
+
94
+ def help=help
95
+ if help.is_a?(Array)
96
+ help.each do |line|
97
+ parser.separator line
98
+ end
99
+ else
100
+ parser.separator help
101
+ end
102
+ end
103
+
104
+ #---
105
+
106
+ def parse_command(args)
107
+ args = args.dup
108
+ error = false
109
+
110
+ self.processed = false
111
+
112
+ option_str(:log_level, nil,
113
+ '--log_level STR',
114
+ 'nucleon.core.util.cli.options.log_level'
115
+ )
116
+ option_str(:encoded_params, false,
117
+ '--encoded STR',
118
+ 'nucleon.core.util.cli.options.encoded'
119
+ )
120
+ parser.on_tail('-h', '--help', CLI.message('nucleon.core.util.cli.options.help')) do
121
+ options[:help] = true
122
+ return
123
+ end
124
+
125
+ parser.parse!(args)
126
+
127
+ Nucleon.log_level = options[:log_level] if options[:log_level]
128
+ parse_encoded
129
+
130
+ remaining_args = args.dup
131
+ arg_messages = []
132
+
133
+ if arguments.empty?
134
+ @arg_settings.each_with_index do |settings, index|
135
+ if index >= args.length
136
+ value = nil
137
+ else
138
+ value = Util::Data.value(args[index])
139
+ end
140
+
141
+ if !value.nil? && settings.has_key?(:allowed)
142
+ allowed = settings[:allowed]
143
+ case allowed
144
+ when Class
145
+ if (allowed == Array)
146
+ value = remaining_args
147
+ remaining_args = []
148
+ end
149
+ unless value.is_a?(allowed)
150
+ arg_messages << CLI.message(settings[:message])
151
+ error = true
152
+ end
153
+ when Array
154
+ unless allowed.include(value)
155
+ arg_messages << CLI.message(settings[:message])
156
+ error = true
157
+ end
158
+ end
159
+ end
160
+
161
+ if value.nil?
162
+ if settings.has_key?(:default)
163
+ value = settings[:default]
164
+ else
165
+ error = true
166
+ end
167
+ end
168
+
169
+ if !value.nil? && settings.has_key?(:block)
170
+ value = settings[:block].call(value)
171
+ error = true if value.nil?
172
+ end
173
+
174
+ break if error
175
+
176
+ remaining_args.shift unless remaining_args.empty?
177
+ self.arguments[settings[:name]] = value
178
+ end
179
+ end
180
+
181
+ if error
182
+ if ! arg_messages.empty?
183
+ parser.warn(CLI.message('nucleon.core.util.cli.parse.error') + "\n\n" + arg_messages.join("\n") + "\n\n" + parser.help)
184
+ else
185
+ parser.warn(CLI.message('nucleon.core.util.cli.parse.error') + "\n\n" + parser.help)
186
+ end
187
+ else
188
+ self.processed = true
189
+ end
190
+
191
+ rescue OptionParser::InvalidOption => e
192
+ parser.warn(e.message + "\n\n" + parser.help)
193
+ end
194
+
195
+ #---
196
+
197
+ def parse_encoded
198
+ if options[:encoded_params]
199
+ encoded_properties = CLI.decode(options[:encoded_params])
200
+
201
+ @arg_settings.each do |settings|
202
+ if encoded_properties.has_key?(settings[:name].to_sym)
203
+ self.arguments[settings[:name]] = encoded_properties.delete(settings[:name].to_sym)
204
+ end
205
+ end
206
+
207
+ encoded_properties.each do |name, value|
208
+ self.options[name] = value unless options.has_key?(name)
209
+ end
210
+ end
211
+ options.delete(:encoded_params)
212
+ end
213
+
214
+ #---
215
+
216
+ def option(name, default, option_str, allowed_values, message_id, config = {})
217
+ config = Config.ensure(config)
218
+ name = name.to_sym
219
+ options[name] = config.get(name, default)
220
+
221
+ message_name = name.to_s + '_message'
222
+ message = CLI.message(message_id, options[name])
223
+
224
+ option_str = Util::Data.array(option_str)
225
+
226
+ if allowed_values
227
+ parser.on(*option_str, allowed_values, config.get(message_name.to_sym, message)) do |value|
228
+ value = yield(value) if block_given?
229
+ options[name] = value unless value.nil?
230
+ end
231
+ else
232
+ parser.on(*option_str, config.get(message_name.to_sym, message)) do |value|
233
+ value = yield(value) if block_given?
234
+ options[name] = value unless value.nil?
235
+ end
236
+ end
237
+ end
238
+
239
+ #---
240
+
241
+ def arg(name, default, allowed_values, message_id, config = {}, &block)
242
+ config = Config.ensure(config)
243
+ name = name.to_sym
244
+
245
+ message_name = name.to_s + '_message'
246
+ message = CLI.message(message_id, arguments[name])
247
+
248
+ settings = {
249
+ :name => name,
250
+ :default => config.get(name, default),
251
+ :message => config.get(message_name.to_sym, message)
252
+ }
253
+ settings[:allowed] = allowed_values if allowed_values
254
+ settings[:block] = block if block
255
+
256
+ settings.delete(:default) if settings[:default].nil?
257
+
258
+ @arg_settings << settings
259
+ end
260
+
261
+ #---
262
+
263
+ def option_bool(name, default, option_str, message_id, config = {})
264
+ option(name, default, option_str, nil, message_id, config) do |value|
265
+ value = Util::Data.value(value)
266
+ if value == true || value == false
267
+ block_given? ? yield(value) : value
268
+ else
269
+ nil
270
+ end
271
+ end
272
+ end
273
+
274
+ #---
275
+
276
+ def arg_bool(name, default, message_id, config = {})
277
+ arg(name, default, nil, message_id, config) do |value|
278
+ value = Util::Data.value(value)
279
+ if value == true || value == false
280
+ block_given? ? yield(value) : value
281
+ else
282
+ nil
283
+ end
284
+ end
285
+ end
286
+
287
+ #---
288
+
289
+ def option_int(name, default, option_str, message_id, config = {})
290
+ option(name, default, option_str, Integer, message_id, config) do |value|
291
+ block_given? ? yield(value) : value
292
+ end
293
+ end
294
+
295
+ #---
296
+
297
+ def arg_int(name, default, message_id, config = {})
298
+ arg(name, default, Integer, message_id, config) do |value|
299
+ block_given? ? yield(value) : value
300
+ end
301
+ end
302
+
303
+ #---
304
+
305
+ def option_float(name, default, option_str, message_id, config = {})
306
+ option(name, default, option_str, Float, message_id, config) do |value|
307
+ block_given? ? yield(value) : value
308
+ end
309
+ end
310
+
311
+ #---
312
+
313
+ def arg_float(name, default, message_id, config = {})
314
+ arg(name, default, Float, message_id, config) do |value|
315
+ block_given? ? yield(value) : value
316
+ end
317
+ end
318
+
319
+ #---
320
+
321
+ def option_str(name, default, option_str, message_id, config = {})
322
+ option(name, default, option_str, nil, message_id, config) do |value|
323
+ block_given? ? yield(value) : value
324
+ end
325
+ end
326
+
327
+ #---
328
+
329
+ def arg_str(name, default, message_id, config = {})
330
+ arg(name, default, nil, message_id, config) do |value|
331
+ block_given? ? yield(value) : value
332
+ end
333
+ end
334
+
335
+ #---
336
+
337
+ def option_array(name, default, option_str, message_id, config = {})
338
+ option(name, default, option_str, Array, message_id, config) do |value|
339
+ block_given? ? yield(value) : value
340
+ end
341
+ end
342
+
343
+ #---
344
+
345
+ def arg_array(name, default, message_id, config = {})
346
+ arg(name, default, Array, message_id, config) do |value|
347
+ block_given? ? yield(value) : value
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end
353
+ end
@@ -0,0 +1,237 @@
1
+
2
+ module Nucleon
3
+ module Util
4
+ class Console
5
+
6
+ @@console_lock = Mutex.new
7
+ @@quiet = false
8
+
9
+ #---
10
+
11
+ COLORS = {
12
+ :clear => "\e[0m",
13
+ :red => "\e[31m",
14
+ :green => "\e[32m",
15
+ :yellow => "\e[33m"
16
+ }
17
+
18
+ COLOR_MAP = {
19
+ :warn => COLORS[:yellow],
20
+ :error => COLORS[:red],
21
+ :success => COLORS[:green]
22
+ }
23
+
24
+ #-----------------------------------------------------------------------------
25
+ # Constructor
26
+
27
+ def initialize(options = {})
28
+ if options.is_a?(String)
29
+ options = { :resource => options }
30
+ end
31
+ config = Config.ensure(options)
32
+
33
+ @resource = config.get(:resource, '')
34
+
35
+ @color = config.get(:color, true)
36
+ @printer = config.get(:printer, :puts)
37
+
38
+ @input = config.get(:input, $stdin)
39
+ @output = config.get(:output, $stdout)
40
+ @error = config.get(:error, $stderr)
41
+
42
+ @delegate = config.get(:console_delegate, nil)
43
+ end
44
+
45
+ #---
46
+
47
+ def inspect
48
+ "#<#{self.class}: #{@resource}>"
49
+ end
50
+
51
+ #-----------------------------------------------------------------------------
52
+ # Accessors / Modifiers
53
+
54
+ attr_accessor :resource, :color, :input, :output, :error, :delegate
55
+
56
+ #---
57
+
58
+ def self.quiet=quiet
59
+ @@quiet = quiet
60
+ end
61
+
62
+ #-----------------------------------------------------------------------------
63
+ # UI functionality
64
+
65
+ def say(type, message, options = {})
66
+ return if @@quiet && ! options[:quiet_override]
67
+ return @delegate.say(type, message, options) if check_delegate('say')
68
+
69
+ defaults = { :new_line => true, :prefix => true }
70
+ options = defaults.merge(options)
71
+ printer = options[:new_line] ? :puts : :print
72
+ channel = type == :error || options[:channel] == :error ? @error : @output
73
+
74
+ options[:sync] = true unless options.has_key?(:sync)
75
+
76
+ render = lambda do
77
+ safe_puts(format_message(type, message, options),
78
+ :channel => channel, :printer => printer)
79
+ end
80
+
81
+ if options[:sync]
82
+ @@console_lock.synchronize do
83
+ render.call
84
+ end
85
+ else
86
+ render.call
87
+ end
88
+ end
89
+
90
+ #---
91
+
92
+ def ask(message, options = {})
93
+ return @delegate.ask(message, options) if check_delegate('ask')
94
+
95
+ options[:new_line] = false if ! options.has_key?(:new_line)
96
+ options[:prefix] = false if ! options.has_key?(:prefix)
97
+ options[:echo] = true if ! options.has_key?(:echo)
98
+
99
+ options[:sync] = true unless options.has_key?(:sync)
100
+
101
+ user_input = nil
102
+
103
+ collect = lambda do
104
+ say(:info, message, Config.ensure(options).import({ :sync => false, :quiet_override => true }).export)
105
+
106
+ if options[:echo]
107
+ user_input = @input.gets.chomp
108
+ else
109
+ require 'io/console'
110
+ user_input = @input.noecho(&:gets).chomp
111
+ end
112
+ safe_puts("\n")
113
+ user_input
114
+ end
115
+
116
+ if options[:sync]
117
+ @@console_lock.synchronize do
118
+ return collect.call
119
+ end
120
+ else
121
+ return collect.call
122
+ end
123
+ end
124
+
125
+ #---
126
+
127
+ def password(type, options = {})
128
+ return @delegate.password(type, options) if check_delegate('password')
129
+
130
+ options[:sync] = true unless options.has_key?(:sync)
131
+
132
+ collect = lambda do
133
+ try_again = true
134
+ password = nil
135
+
136
+ while try_again
137
+ # Get and check a password from the keyboard
138
+ password = ask("Enter #{type} password: ", { :echo => false, :sync => false })
139
+ confirmation_password = ask("Confirm #{type} password: ", { :echo => false, :sync => false })
140
+
141
+ if password != confirmation_password
142
+ choice = ask('Passwords do not match! Try again? (Y|N): ', { :sync => false })
143
+ try_again = choice.upcase == "Y"
144
+ password = nil unless try_again
145
+ else
146
+ try_again = false
147
+ end
148
+ end
149
+ password
150
+ end
151
+
152
+ if options[:sync]
153
+ @@console_lock.synchronize do
154
+ return collect.call
155
+ end
156
+ else
157
+ return collect.call
158
+ end
159
+ end
160
+
161
+ #-----------------------------------------------------------------------------
162
+
163
+ def info(message, *args)
164
+ return @delegate.info(message, *args) if check_delegate('info')
165
+ say(:info, message, *args)
166
+ end
167
+
168
+ #---
169
+
170
+ def warn(message, *args)
171
+ return @delegate.warn(message, *args) if check_delegate('warn')
172
+ say(:warn, message, *args)
173
+ end
174
+
175
+ #---
176
+
177
+ def error(message, *args)
178
+ return @delegate.error(message, *args) if check_delegate('error')
179
+ say(:error, message, *args)
180
+ end
181
+
182
+ #---
183
+
184
+ def success(message, *args)
185
+ return @delegate.success(message, *args) if check_delegate('success')
186
+ say(:success, message, *args)
187
+ end
188
+
189
+ #-----------------------------------------------------------------------------
190
+ # Utilities
191
+
192
+ def format_message(type, message, options = {})
193
+ return @delegate.format_message(type, message, options) if check_delegate('format_message')
194
+ return '' if message.to_s.strip.empty?
195
+
196
+ if @resource && ! @resource.empty? && options[:prefix]
197
+ prefix = "[#{@resource}]"
198
+ end
199
+ message = "#{prefix} #{message}".lstrip.gsub(/\n+$/, '')
200
+
201
+ if @color
202
+ if options.has_key?(:color)
203
+ color = COLORS[options[:color]]
204
+ message = "#{color}#{message}#{COLORS[:clear]}"
205
+ else
206
+ message = "#{COLOR_MAP[type]}#{message}#{COLORS[:clear]}" if COLOR_MAP[type]
207
+ end
208
+ end
209
+ return message
210
+ end
211
+
212
+ #---
213
+
214
+ def safe_puts(message = nil, options = {})
215
+ return @delegate.safe_puts(message, options) if check_delegate('safe_puts')
216
+
217
+ message ||= ""
218
+ options = {
219
+ :channel => @output,
220
+ :printer => @printer,
221
+ }.merge(options)
222
+
223
+ begin
224
+ options[:channel].send(options[:printer], message)
225
+ rescue Errno::EPIPE
226
+ return
227
+ end
228
+ end
229
+
230
+ #-----------------------------------------------------------------------------
231
+
232
+ def check_delegate(method)
233
+ return ( @delegate && @delegate.respond_to?(method.to_s) )
234
+ end
235
+ end
236
+ end
237
+ end