nucleon 0.1.0 → 0.1.1

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.
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