class Nucleon::Plugin::Action
Public Class Methods
action_help(action = nil, extended_help = false)
click to toggle source
# File lib/core/plugin/action.rb 627 def self.action_help(action = nil, extended_help = false) 628 action_index = action_index(false).export 629 provider_index = {} 630 processed_actions = {} 631 632 last_namespace = nil 633 last_group = nil 634 multiple_found = false 635 636 command_width = 0 637 namespace_command_width = {} 638 639 output = '' 640 641 if action 642 if action.empty? 643 output << cyan(sprintf("\n%s\n", I18n.t('nucleon.core.exec.help.no_actions_found'))) 644 else 645 multiple_found = true 646 output << cyan(sprintf("\n%s\n", I18n.t('nucleon.core.exec.help.multiple_actions_found'))) 647 648 action.each do |info| 649 provider_index[info[:provider]] = true 650 end 651 end 652 end 653 654 action_index.each do |action_id, info| 655 if ! multiple_found || provider_index.has_key?(info[:provider]) 656 action = Nucleon.action(info[:provider], { :settings => {}, :quiet => true, :help => true }) 657 command_text = action.help 658 659 command_size = command_text.gsub(/\e\[(\d+)m/, '').size 660 command_width = [ command_width, command_size + 2 ].max 661 662 namespace = info[:description][:namespace] 663 664 namespace_command_width[namespace] = 0 unless namespace_command_width.has_key?(namespace) 665 namespace_command_width[namespace] = [ namespace_command_width[namespace], command_size + 2 ].max 666 667 if extended_help 668 help_text = '' 669 info[:description][:help].split("\n").each do |line| 670 break if ! help_text.empty? && line.empty? 671 help_text << ' ' + line + "\n" 672 end 673 else 674 help_text = nil 675 end 676 677 processed_actions[action_id] = { 678 :info => info, 679 :command => command_text, 680 :help => help_text 681 } 682 end 683 end 684 685 processed_actions.each do |action_id, info| 686 command_text = info[:command] 687 help_text = info[:help] 688 info = info[:info] 689 namespace = info[:description][:namespace] 690 group = info[:description][:group] 691 692 group_id = group.is_a?(Array) ? group.flatten.join('::') : group.to_s 693 group_id = '' unless group_id 694 695 output << "\n" if group_id != last_group 696 697 if namespace != last_namespace 698 output << "\n----------------------------------------------------\n" if help_text 699 output << sprintf("\n %s:\n\n", I18n.t('nucleon.core.exec.help.action_group', { :namespace => purple(namespace) })) 700 end 701 702 if help_text 703 output << " " + render_colorized(command_text, namespace_command_width[namespace]) + " -- " + blue(info[:description][:description]) + "\n" 704 output << "\n#{help_text}\n" 705 else 706 output << " " + render_colorized(command_text, command_width) + " -- " + blue(info[:description][:description]) + "\n" 707 end 708 709 last_namespace = namespace 710 last_group = group_id 711 end 712 output 713 end
action_index(tree = true)
click to toggle source
# File lib/core/plugin/action.rb 523 def self.action_index(tree = true) 524 action_config = Config.new 525 action_index = Config.new 526 527 generate_index = lambda do |info, parents = nil| 528 groups = info.keys - [ :_weight, :_weights ] 529 groups = groups.sort do |a, b| 530 info[b][:_weight] <=> info[a][:_weight] 531 end 532 533 groups.each do |group| 534 data = info[group] 535 536 if data.is_a?(Hash) && data.has_key?(:_weights) 537 sub_parents = parents.nil? ? [ group ] : [ parents, group ].flatten 538 generate_index.call(data, sub_parents) 539 else 540 keys = tree ? [ parents, group ] : [ parents, group ].flatten.join('::') 541 action_index.set(keys, data) 542 end 543 end 544 end 545 546 Nucleon.loaded_plugins(:nucleon, :action).each do |provider, data| 547 description = data[:class].describe 548 549 unless description.nil? || !description.is_a?(Hash) 550 data[:description] = description 551 data[:_weight] = description[:weight] 552 553 keys = [ description[:namespace], description[:group], description[:action] ].flatten.compact 554 action_config.set(keys, data) 555 556 keys.pop 557 558 while ! keys.empty? 559 group_config = action_config.get(keys) 560 561 if group_config.has_key?(:_weights) 562 group_config[:_weights].push(description[:weight]) 563 else 564 action_config.set([ keys, :_weights ], [ description[:weight] ]) 565 end 566 action_config.set([ keys, :_weight ], group_config[:_weights].inject(0.0) { |sum, el| sum + el } / group_config[:_weights].size) 567 keys.pop 568 end 569 end 570 end 571 572 generate_index.call(action_config.export) 573 action_index 574 end
components(search)
click to toggle source
# File lib/core/plugin/action.rb 511 def self.components(search) 512 components = [] 513 514 array(search).each do |element| 515 break if element.match(/^\-+/) 516 components << element 517 end 518 components 519 end
describe(group = nil, action = 'unknown', weight = -1000, description = nil, help = nil)
click to toggle source
# File lib/core/plugin/action.rb 15 def self.describe(group = nil, action = 'unknown', weight = -1000, description = nil, help = nil) 16 describe_base(group, action, weight, description, help) 17 end
describe_base(group = nil, action = 'unknown', weight = -1000, description = nil, help = nil, provider_override = nil)
click to toggle source
# File lib/core/plugin/action.rb 19 def self.describe_base(group = nil, action = 'unknown', weight = -1000, description = nil, help = nil, provider_override = nil) 20 if provider_override 21 provider_override = provider_override.to_s.gsub('_', '.') 22 description_id = "#{namespace}.action.#{provider_override}.description" 23 help_id = "#{namespace}.action.#{provider_override}.help" 24 else 25 if group 26 group_name = Util::Data.array(group).join('.') 27 description_id = "#{namespace}.action.#{group_name}.#{action}.description" 28 help_id = "#{namespace}.action.#{group_name}.#{action}.help" 29 else 30 description_id = "#{namespace}.action.#{action}.description" 31 help_id = "#{namespace}.action.#{action}.help" 32 end 33 end 34 35 { 36 :namespace => namespace, 37 :weight => weight, 38 :group => group, 39 :action => action, 40 :description => description ? description : I18n.t(description_id), 41 :help => help ? help : I18n.t(help_id) 42 } 43 end
exec(provider, options, quiet = true, display_errors = true, state = nil)
click to toggle source
# File lib/core/plugin/action.rb 129 def self.exec(provider, options, quiet = true, display_errors = true, state = nil) 130 exec_safe(provider, { :settings => Config.ensure(options), :quiet => quiet }, display_errors, state) 131 end
exec_cli(provider, args, quiet = false, name = :nucleon, display_errors = true, state = nil)
click to toggle source
# File lib/core/plugin/action.rb 133 def self.exec_cli(provider, args, quiet = false, name = :nucleon, display_errors = true, state = nil) 134 exec_safe(provider, { :args => args, :quiet => quiet, :executable => name }, display_errors, state) 135 end
exec_safe(provider, options, display_errors = true, state = nil)
click to toggle source
# File lib/core/plugin/action.rb 99 def self.exec_safe(provider, options, display_errors = true, state = nil) 100 begin 101 state = State.new(Nucleon.code.unknown_status) unless state 102 logger = Nucleon.logger 103 104 logger.info("Running nucleon action #{provider} with #{options.inspect}") 105 106 state.action = Nucleon.action(provider, options) 107 state.action.execute 108 109 state.status = state.action.status if state.action.status.is_a?(Integer) 110 state.result = state.action.result 111 112 rescue => error # This does NOT catch interrupts 113 state.error = error 114 115 if display_errors 116 logger.error("Nucleon action #{provider} experienced an error:") 117 logger.error(state.error.inspect) 118 logger.error(state.error.message) 119 logger.error(Nucleon::Util::Data.to_yaml(state.error.backtrace)) 120 121 Nucleon.ui.error(state.error.message, { :prefix => false }) if state.error.message 122 end 123 124 state.action.finalize_execution(false) if state.action 125 end 126 state 127 end
namespace()
click to toggle source
# File lib/core/plugin/action.rb 47 def self.namespace 48 :nucleon 49 end
render_colorized(text, length = 0)
click to toggle source
# File lib/core/plugin/action.rb 717 def self.render_colorized(text, length = 0) 718 command_size = text.gsub(/\e\[(\d+)m/, '').size 719 remaining = [ length - command_size, 0 ].max 720 text + sprintf("%#{remaining}s", ' ') 721 end
search_actions(search_components)
click to toggle source
# File lib/core/plugin/action.rb 578 def self.search_actions(search_components) 579 action_components = components(search_components) 580 action_index = action_index(false).export 581 actions_found = [] 582 final_components = [] 583 584 search_action = lambda do |components| 585 unless components.empty? 586 index = action_index 587 action_id = components.is_a?(Array) ? components.flatten.join('::') : components 588 action_id_pattern = action_id.gsub('::', ':.*:') 589 590 # Check for exact matches 591 index.each do |loaded_action_id, loaded_action_info| 592 if loaded_action_id.match(/^[^\:]+\:\:#{action_id.gsub(/\-/, '\-')}$/) 593 loaded_action_info[:action_id] = loaded_action_id 594 actions_found << loaded_action_info 595 break 596 end 597 end 598 599 if actions_found.empty? 600 # Check for similarly named actions 601 index.each do |loaded_action_id, loaded_action_info| 602 if loaded_action_id.match(/(^|\:)#{action_id_pattern.gsub(/\-/, '\-')}(\:|$)/) 603 loaded_action_info[:action_id] = loaded_action_id 604 actions_found << loaded_action_info 605 end 606 end 607 end 608 end 609 if components.is_a?(Array) && ! components.empty? && actions_found.empty? 610 components.pop 611 final_components = components 612 search_action.call(components) 613 else 614 final_components = components 615 end 616 end 617 618 search_action.call(action_components) unless action_components.empty? 619 620 { :actions => actions_found.size == 1 ? actions_found[0] : actions_found, 621 :components => final_components 622 } 623 end
Public Instance Methods
arguments()
click to toggle source
# File lib/core/plugin/action.rb 263 def arguments 264 [] 265 end
cleanup() { || ... }
click to toggle source
# File lib/core/plugin/action.rb 491 def cleanup 492 logger.info("Running cleanup for action #{plugin_provider}") 493 494 yield if block_given? 495 496 # Nothing to do right now 497 extension(:cleanup) 498 end
config()
click to toggle source
# File lib/core/plugin/action.rb 209 def config 210 get(:config) 211 end
config_subset(names)
click to toggle source
# File lib/core/plugin/action.rb 215 def config_subset(names) 216 Util::Data.subset(config, names) 217 end
configure() { |action_info| ... }
click to toggle source
# File lib/core/plugin/action.rb 269 def configure 270 action_info = index_config 271 272 yield(action_info) if block_given? 273 274 group = array(action_info[:description][:group]) 275 action = cyan(action_info[:description][:action]) 276 277 if ! group.empty? 278 group = green(group.join(' ').strip) 279 usage = "#{group} #{action} " 280 else 281 usage = "#{action} " 282 end 283 284 arguments.each do |arg| 285 arg_config = config[arg.to_sym] 286 287 arg_prefix = arg_config.default ? '[' : '' 288 arg_suffix = arg_config.default ? ']' : '' 289 290 if arg_config.type == :array 291 usage << "#{arg_prefix}<#{arg}> ...#{arg_suffix}" 292 else 293 usage << "#{arg_prefix}<#{arg}>#{arg_suffix} " 294 end 295 end 296 myself.usage = yellow(usage) 297 myself 298 end
execute(skip_validate = false, skip_hooks = false) { || ... }
click to toggle source
# File lib/core/plugin/action.rb 441 def execute(skip_validate = false, skip_hooks = false) 442 logger.info("Executing action #{plugin_provider}") 443 444 myself.status = code.success 445 myself.result = nil 446 447 if processed? 448 if skip_validate || validate 449 yield if block_given? && ( skip_hooks || extension_check(:exec_init) ) 450 else 451 puts "\n" + I18n.t('nucleon.core.exec.help.usage') + ': ' + help + "\n" unless quiet? 452 myself.status = code.validation_failed 453 skip_hooks = true 454 end 455 finalize_execution(skip_hooks) 456 else 457 if @parser.options[:help] 458 myself.status = code.help_wanted 459 else 460 myself.status = code.action_unprocessed 461 end 462 finalize_execution(true) 463 end 464 end
finalize_execution(skip_hooks = false)
click to toggle source
# File lib/core/plugin/action.rb 468 def finalize_execution(skip_hooks = false) 469 begin 470 myself.status = extension_set(:exec_exit, status) unless skip_hooks 471 ensure 472 cleanup 473 end 474 475 myself.status = code.unknown_status unless myself.status.is_a?(Integer) 476 477 if processed? && myself.status != code.success 478 logger.warn("Execution failed for #{plugin_provider} with status #{status}") 479 warn(Codes.render_status(status), { :i18n => false }) 480 end 481 end
help()
click to toggle source
# File lib/core/plugin/action.rb 312 def help 313 return @parser.help if @parser 314 usage 315 end
ignore()
click to toggle source
# File lib/core/plugin/action.rb 255 def ignore 256 [] 257 end
index_config()
click to toggle source
# File lib/core/plugin/action.rb 190 def index_config 191 action_info = nil 192 self.class.action_index(false).export.each do |action_id, info| 193 if info[:provider] == plugin_provider 194 action_info = info 195 break 196 end 197 end 198 Config.ensure(action_info) 199 end
namespace()
click to toggle source
# File lib/core/plugin/action.rb 203 def namespace 204 self.class.namespace 205 end
normalize(reload) { || ... }
click to toggle source
# File lib/core/plugin/action.rb 139 def normalize(reload) 140 args = array(delete(:args, [])) 141 help = delete(:help, false) 142 143 unless reload 144 @action_interface = Util::Liquid.new do |method, method_args| 145 options = {} 146 options = method_args[0] if method_args.length > 0 147 148 quiet = true 149 quiet = method_args[1] if method_args.length > 1 150 151 myself.class.exec(method, options, quiet) 152 end 153 154 set(:config, Config.new) 155 156 if get(:settings, nil) 157 # Internal processing 158 configure 159 set(:processed, true) 160 set(:settings, Config.ensure(get(:settings))) 161 162 Nucleon.log_level = settings[:log_level] if settings.has_key?(:log_level) 163 else 164 # External processing 165 set(:settings, Config.new) 166 configure 167 parse_base(args) 168 end 169 170 yield if block_given? && ! help 171 end 172 end
options()
click to toggle source
# File lib/core/plugin/action.rb 259 def options 260 config.keys - arguments - ignore 261 end
parse(parser)
click to toggle source
# File lib/core/plugin/action.rb 373 def parse(parser) 374 375 generate = lambda do |format, name| 376 formats = [ :option, :arg ] 377 types = parse_types 378 name = name.to_sym 379 380 if config.export.has_key?(name) && formats.include?(format.to_sym) 381 option_config = config[name] 382 type = option_config.type 383 default = option_config.default 384 locale = option_config.locale 385 386 if types.include?(type.to_sym) 387 value_label = "#{type.to_s.upcase}" 388 389 if type == :bool 390 parser.send("option_#{type}", name, default, "--[no-]#{name}", locale) 391 elsif format == :arg 392 parser.send("#{format}_#{type}", name, default, locale) 393 else 394 if type == :array 395 parser.send("option_#{type}", name, default, "--#{name} #{value_label},...", locale) 396 else 397 parser.send("option_#{type}", name, default, "--#{name} #{value_label}", locale) 398 end 399 end 400 end 401 end 402 end 403 404 #--- 405 406 options.each do |name| 407 generate.call(:option, name) 408 end 409 410 arguments.each do |name| 411 generate.call(:arg, name) 412 end 413 end
parse_base(args)
click to toggle source
# File lib/core/plugin/action.rb 330 def parse_base(args) 331 logger.info("Parsing action #{plugin_provider} with: #{args.inspect}") 332 333 action_info = index_config 334 335 help_text = '' 336 action_info[:description][:help].split("\n").each do |line| 337 help_text << ' ' + green(line) + "\n" 338 end 339 340 @parser = Util::CLI::Parser.new(args, usage, "\n#{help_text}\n") do |parser| 341 parser.strict = strict? 342 343 parse(parser) 344 extension(:parse, { :parser => parser, :config => config }) 345 end 346 347 if @parser 348 if @parser.processed 349 set(:processed, true) 350 settings.import(Util::Data.merge([ @parser.extra, @parser.options, @parser.arguments ], true)) 351 logger.debug("Parse successful") 352 353 elsif @parser.options[:help] && ! quiet? 354 executable = delete(:executable, '') 355 puts I18n.t('nucleon.core.exec.help.usage') + ": " + executable.to_s + ' ' + help + "\n" 356 357 else 358 if @parser.options[:help] 359 logger.debug("Help wanted but running in silent mode") 360 else 361 logger.warn("Parse failed for unknown reasons") 362 end 363 end 364 end 365 end
parse_types()
click to toggle source
# File lib/core/plugin/action.rb 369 def parse_types 370 [ :bool, :int, :float, :str, :array ] 371 end
processed?()
click to toggle source
# File lib/core/plugin/action.rb 183 def processed? 184 get(:processed, false) 185 end
register(name, type, default = nil, locale = nil, &code)
click to toggle source
# File lib/core/plugin/action.rb 231 def register(name, type, default = nil, locale = nil, &code) 232 name = name.to_sym 233 234 if code 235 option = Option.new(namespace, registration_provider, name, type, default, locale) do |value, success| 236 code.call(value, success) 237 end 238 else 239 option = Option.new(namespace, registration_provider, name, type, default, locale) 240 end 241 242 config[name] = option 243 settings[name] = option.default if settings[name].nil? 244 end
registration_provider()
click to toggle source
# File lib/core/plugin/action.rb 227 def registration_provider 228 plugin_provider 229 end
remove(names)
click to toggle source
# File lib/core/plugin/action.rb 248 def remove(names) 249 Util::Data.rm_keys(config, names) 250 Util::Data.rm_keys(settings, names) 251 end
render_options()
click to toggle source
Calls superclass method
# File lib/core/plugin/action.rb 503 def render_options 504 options = super 505 options.merge(settings.export) 506 end
result()
click to toggle source
# File lib/core/plugin/action.rb 323 def result 324 get(:result, nil) 325 end
result=(result)
click to toggle source
# File lib/core/plugin/action.rb 319 def result=result 320 set(:result, result) 321 end
run()
click to toggle source
# File lib/core/plugin/action.rb 485 def run 486 @action_interface 487 end
settings()
click to toggle source
# File lib/core/plugin/action.rb 221 def settings 222 get(:settings) 223 end
strict?()
click to toggle source
# File lib/core/plugin/action.rb 177 def strict? 178 true # Override in providers if needed (allow extra options if false) 179 end
usage()
click to toggle source
# File lib/core/plugin/action.rb 306 def usage 307 get(:usage, '') 308 end
usage=(usage)
click to toggle source
# File lib/core/plugin/action.rb 302 def usage=usage 303 set(:usage, usage) 304 end
validate(*args)
click to toggle source
# File lib/core/plugin/action.rb 417 def validate(*args) 418 # TODO: Add extension hooks and logging 419 420 # Validate all of the configurations 421 success = true 422 config.export.each do |name, option| 423 unless ignore.include?(name) 424 success = false unless option.validate(settings[name], *args) 425 end 426 end 427 if success 428 # Check for missing arguments (in case of internal execution mode) 429 arguments.each do |name| 430 if settings[name.to_sym].nil? 431 warn('nucleon.core.exec.errors.missing_argument', { :name => name }) 432 success = false 433 end 434 end 435 end 436 success 437 end