hobo-inviqa 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/Gemfile +4 -0
  2. data/Gemfile.lock +93 -0
  3. data/Guardfile +14 -0
  4. data/Hobofile +34 -0
  5. data/Rakefile +2 -0
  6. data/bin/hobo +23 -0
  7. data/features/deps.feature +43 -0
  8. data/features/hobo/basic.feature +30 -0
  9. data/features/hobo/help.feature +12 -0
  10. data/features/hobo/subcommands.feature +16 -0
  11. data/features/seed/plant.feature +64 -0
  12. data/features/step_definitions/seed.rb +11 -0
  13. data/features/support/env.rb +6 -0
  14. data/features/vm.feature +0 -0
  15. data/hobo.gemspec +37 -0
  16. data/lib/hobo.rb +44 -0
  17. data/lib/hobo/cli.rb +185 -0
  18. data/lib/hobo/config/file.rb +21 -0
  19. data/lib/hobo/error_handlers/debug.rb +9 -0
  20. data/lib/hobo/error_handlers/friendly.rb +52 -0
  21. data/lib/hobo/errors.rb +60 -0
  22. data/lib/hobo/help_formatter.rb +111 -0
  23. data/lib/hobo/helper/file_locator.rb +39 -0
  24. data/lib/hobo/helper/shell.rb +59 -0
  25. data/lib/hobo/lib/seed/project.rb +41 -0
  26. data/lib/hobo/lib/seed/replacer.rb +57 -0
  27. data/lib/hobo/lib/seed/seed.rb +43 -0
  28. data/lib/hobo/metadata.rb +28 -0
  29. data/lib/hobo/patches/rake.rb +56 -0
  30. data/lib/hobo/patches/slop.rb +22 -0
  31. data/lib/hobo/paths.rb +49 -0
  32. data/lib/hobo/tasks/debug.rb +22 -0
  33. data/lib/hobo/tasks/deps.rb +45 -0
  34. data/lib/hobo/tasks/seed.rb +43 -0
  35. data/lib/hobo/tasks/tools.rb +13 -0
  36. data/lib/hobo/tasks/vm.rb +49 -0
  37. data/lib/hobo/ui.rb +96 -0
  38. data/lib/hobo/util.rb +7 -0
  39. data/lib/hobo/version.rb +3 -0
  40. data/spec/hobo/cli_spec.rb +135 -0
  41. data/spec/hobo/config/file_spec.rb +48 -0
  42. data/spec/hobo/error_handlers/debug_spec.rb +10 -0
  43. data/spec/hobo/error_handlers/friendly_spec.rb +81 -0
  44. data/spec/hobo/error_spec.rb +0 -0
  45. data/spec/hobo/help_formatter_spec.rb +131 -0
  46. data/spec/hobo/helpers/file_locator_spec.rb +7 -0
  47. data/spec/hobo/helpers/shell_spec.rb +7 -0
  48. data/spec/hobo/lib/seed/project_spec.rb +83 -0
  49. data/spec/hobo/lib/seed/replacer_spec.rb +47 -0
  50. data/spec/hobo/lib/seed/seed_spec.rb +95 -0
  51. data/spec/hobo/metadata_spec.rb +46 -0
  52. data/spec/hobo/patches/rake_spec.rb +0 -0
  53. data/spec/hobo/paths_spec.rb +77 -0
  54. data/spec/hobo/ui_spec.rb +64 -0
  55. data/spec/spec_helper.rb +6 -0
  56. metadata +355 -0
data/lib/hobo/cli.rb ADDED
@@ -0,0 +1,185 @@
1
+ module Hobo
2
+
3
+ class Halt < Error
4
+ end
5
+
6
+ class Cli
7
+ attr_accessor :slop, :help_formatter
8
+
9
+ def initialize opts = {}
10
+ @opts = opts
11
+ @slop = opts[:slop] || Slop.new
12
+ @help_formatter = opts[:help] || Hobo::HelpFormatter.new(@slop)
13
+ @help_opts = {}
14
+ end
15
+
16
+ def start argv = ARGV
17
+ load_hobofile
18
+
19
+ tasks = structure_tasks Hobo::Metadata.metadata.keys
20
+ args = fix_args_with_equals argv
21
+ define_global_opts @slop
22
+
23
+ begin
24
+ # Parse out global args first
25
+ @slop.parse! args
26
+ opts = @slop.to_hash
27
+ @help_opts[:all] = opts[:all]
28
+ Hobo.ui.interactive = !(opts[:'non-interactive'] == true)
29
+
30
+ @slop.add_callback :empty do
31
+ show_help
32
+ end
33
+
34
+ # Necessary to make command level help work
35
+ args.push "--help" if @slop.help?
36
+
37
+ @help_formatter.command_map = define_tasks(tasks, @slop)
38
+
39
+ remaining = @slop.parse! args
40
+ raise Hobo::InvalidCommandOrOpt.new remaining.join(" "), self if remaining.size > 0
41
+
42
+ show_help if @slop.help?
43
+ rescue Halt
44
+ # NOP
45
+ end
46
+
47
+ return 0
48
+ end
49
+
50
+ def show_help(opts = {})
51
+ Hobo.ui.info @help_formatter.help(@help_opts.merge(opts))
52
+ halt
53
+ end
54
+
55
+ private
56
+
57
+ def load_hobofile
58
+ if Hobo.in_project? && File.exists?(Hobo.hobofile_path)
59
+ load Hobo.hobofile_path
60
+ end
61
+ end
62
+
63
+ def define_global_opts slop
64
+ slop.on '--debug', 'Enable debugging'
65
+ slop.on '-a', '--all', 'Show hidden commands'
66
+ slop.on '-h', '--help', 'Display help'
67
+ slop.on '--non-interactive', 'Run non-interactively. Defaults will be automaticall used where possible.'
68
+
69
+ slop.on '-v', '--version', 'Print version information' do
70
+ Hobo.ui.info "Hobo version #{Hobo::VERSION}"
71
+ halt
72
+ end
73
+ end
74
+
75
+ def halt
76
+ raise Halt.new
77
+ end
78
+
79
+ def define_tasks structured_list, scope, stack = [], map = {}
80
+ structured_list.each do |k, v|
81
+ name = (stack + [k]).join(':')
82
+ new_stack = stack + [k]
83
+ map[name] = if v.size == 0
84
+ define_command(name, scope, new_stack)
85
+ else
86
+ define_namespace(name, scope, new_stack, v, map)
87
+ end
88
+ end
89
+ return map
90
+ end
91
+
92
+ # Map rake namespace to a Slop command
93
+ def define_namespace name, scope, stack, subtasks, map
94
+ metadata = Hobo::Metadata.metadata[name]
95
+ hobo = self
96
+ new_scope = nil
97
+
98
+ scope.instance_eval do
99
+ new_scope = command stack.last do
100
+
101
+ description metadata[:desc]
102
+ long_description metadata[:long_desc]
103
+ hidden metadata[:hidden]
104
+ project_only metadata[:project_only]
105
+
106
+ # NOP; run runs help anyway
107
+ on '-h', '--help', 'Display help' do end
108
+
109
+ run do |opts, args|
110
+ hobo.show_help(target: name)
111
+ end
112
+ end
113
+ end
114
+
115
+ define_tasks subtasks, new_scope, stack, map
116
+ return new_scope
117
+ end
118
+
119
+ # Map rake task to a Slop command
120
+ def define_command name, scope, stack
121
+ metadata = Hobo::Metadata.metadata[name]
122
+ hobo = self
123
+ new_scope = nil
124
+
125
+ scope.instance_eval do
126
+ new_scope = command stack.last do
127
+ task = Rake::Task[name]
128
+
129
+ description metadata[:desc]
130
+ long_description metadata[:long_desc]
131
+ arg_list task.arg_names
132
+ hidden metadata[:hidden]
133
+ project_only metadata[:project_only]
134
+
135
+ metadata[:opts].each do |opt|
136
+ on *opt
137
+ end if metadata[:opts]
138
+
139
+ on '-h', '--help', 'Display help' do
140
+ hobo.show_help(target: name)
141
+ end
142
+
143
+ run do |opts, args|
144
+ raise ::Hobo::ProjectOnlyError.new if opts.project_only && !Hobo.in_project?
145
+ task.opts = opts.to_hash
146
+ raise ::Hobo::MissingArgumentsError.new(name, args, hobo) if args && task.arg_names.length > args.length
147
+ task.invoke *args
148
+ args.pop(task.arg_names.size)
149
+ task.opts = nil
150
+ end
151
+ end
152
+ end
153
+
154
+ return new_scope
155
+ end
156
+
157
+ # Expand flat task list in to hierarchy (non-recursive)
158
+ def structure_tasks list
159
+ out = {}
160
+ list.each do |name|
161
+ ref = out
162
+ name = name.split(":")
163
+ name.each do |n|
164
+ ref[n] ||= {}
165
+ ref = ref[n]
166
+ end
167
+ end
168
+ out
169
+ end
170
+
171
+ # Slop badly handles assignment args passed as --arg=val
172
+ # This hack fixes that by making them --arg val
173
+ def fix_args_with_equals args
174
+ items = []
175
+ args.each do |item|
176
+ if item.match /^-.*\=/
177
+ item.split('=').each { |i| items.push i }
178
+ else
179
+ items.push item
180
+ end
181
+ end
182
+ return items
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,21 @@
1
+ require 'yaml'
2
+
3
+ module Hobo
4
+ module Config
5
+ class File
6
+ def self.save(file, config)
7
+ dir = ::File.dirname file
8
+ FileUtils.mkdir_p dir unless ::File.exists? dir
9
+ ::File.open(file, 'w+') do |f|
10
+ f.puts config.to_yaml
11
+ end
12
+ end
13
+
14
+ def self.load(file)
15
+ config = ::File.exists?(file) ? YAML.load_file(file) : {}
16
+ raise "Invalid hobo configuration (#{file})" unless config
17
+ return config
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Hobo
2
+ module ErrorHandlers
3
+ class Debug
4
+ def handle error
5
+ raise error
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,52 @@
1
+ require 'tmpdir' # Needed for Dir.tmpdir
2
+
3
+ module Hobo
4
+ module ErrorHandlers
5
+ class Friendly
6
+ def handle error
7
+ log_file = File.join(Dir.tmpdir, 'hobo_error.log')
8
+
9
+ # Not possible to match Interrupt class unless we use class name as string for some reason!
10
+ case error.class.to_s
11
+ when "Interrupt"
12
+ Hobo.ui.warning "\n\nCaught Interrupt. Aborting\n"
13
+ return 1
14
+ when "Hobo::ExternalCommandError"
15
+ FileUtils.cp error.output.path, log_file
16
+ Hobo.ui.error <<-ERROR
17
+
18
+ The following external command appears to have failed (exit status #{error.exit_code}):
19
+ #{error.command}
20
+
21
+ The output of the command has been logged to #{log_file}
22
+ ERROR
23
+ return 3
24
+ when "Hobo::InvalidCommandOrOpt"
25
+ Hobo.ui.error "\n#{error.message}"
26
+ Hobo.ui.info error.cli.help_formatter.help if error.cli
27
+ return 4
28
+ when "Hobo::MissingArgumentsError"
29
+ Hobo.ui.error "\n#{error.message}"
30
+ Hobo.ui.info error.cli.help_formatter.help(target: error.command) if error.cli
31
+ return 5
32
+ when "Hobo::UserError"
33
+ Hobo.ui.error "\n#{error.message}\n"
34
+ return 6
35
+ when "Hobo::ProjectOnlyError"
36
+ Hobo.ui.error "\nHobo requires you to be in a project directory for this command!\n"
37
+ return 7
38
+ else
39
+ File.write(log_file, "(#{error.class}) #{error.message}\n\n#{error.backtrace.join("\n")}")
40
+ Hobo.ui.error <<-ERROR
41
+
42
+ An unexpected error has occured:
43
+ #{error.message}
44
+
45
+ The backtrace has been logged to #{log_file}
46
+ ERROR
47
+ return 128
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,60 @@
1
+ module Hobo
2
+ class Error < StandardError
3
+ end
4
+
5
+ class RubyVersionError < Error
6
+ def initialize
7
+ super("Ruby 1.9+ is required to run hobo")
8
+ end
9
+ end
10
+
11
+ class MissingDependencies < Error
12
+ def initialize deps
13
+ deps.map! { |dep| " - #{dep}"}
14
+ super("Hobo requires the following commands to be available on your path:\n\n" + deps.join("\n"))
15
+ end
16
+ end
17
+
18
+ class InvalidCommandOrOpt < Error
19
+ attr_accessor :command, :cli
20
+ def initialize command, cli = nil
21
+ @command = command
22
+ @cli = cli
23
+ super("Invalid command or option specified: '#{command}'")
24
+ end
25
+ end
26
+
27
+ class MissingArgumentsError < Error
28
+ attr_accessor :command, :cli
29
+ def initialize command, args, cli = nil
30
+ @command = command
31
+ @args = args
32
+ @cli = cli
33
+ super("Not enough arguments for #{command}")
34
+ end
35
+ end
36
+
37
+ class ExternalCommandError < Error
38
+ attr_accessor :command, :exit_code, :output
39
+
40
+ def initialize command, exit_code, output
41
+ @command = command
42
+ @exit_code = exit_code
43
+ @output = output
44
+ super("'#{command}' returned exit code #{exit_code}")
45
+ end
46
+ end
47
+
48
+ class UserError < Error
49
+ end
50
+
51
+ class ProjectOnlyError < Error
52
+ end
53
+
54
+ class NonInteractiveError < Error
55
+ def initialize question
56
+ @question = question
57
+ super("A task requested input from user but hobo is in non-interactive mode")
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,111 @@
1
+ module Hobo
2
+ class HelpFormatter
3
+ attr_accessor :command_map
4
+ ALIGN_PAD = 4
5
+
6
+ def initialize global
7
+ @global = global
8
+ @command_map = {}
9
+ end
10
+
11
+ def help opts = {}
12
+ target = opts[:target] || nil
13
+ output = [""]
14
+
15
+ command = @command_map[target]
16
+
17
+ if !command
18
+ command_opts = []
19
+ commands = commands_for(@global, opts)
20
+ command = @global
21
+ else
22
+ command_opts = options_for(command)
23
+ commands = commands_for(command, opts)
24
+ end
25
+
26
+ global_opts = options_for(@global)
27
+
28
+ align_to = longest([global_opts, command_opts, commands]) + ALIGN_PAD
29
+
30
+ if command != @global
31
+ description = [ command.long_description, command.description ].compact.first
32
+ output.push Hobo.ui.color("#{description}\n", :description) if description
33
+ end
34
+
35
+ output.push section("Usage", [usage(command, target)])
36
+ output.push section("Global options", global_opts, align_to)
37
+ output.push section("Command options", command_opts, align_to)
38
+ output.push section("Commands", commands, align_to)
39
+
40
+ return output.compact.join("\n")
41
+ end
42
+
43
+ private
44
+
45
+ def longest inputs
46
+ inputs.map do |input|
47
+ next unless input.is_a? Array
48
+ if input.size == 0
49
+ 0
50
+ else
51
+ input.map(&:first).map(&:size).max
52
+ end
53
+ end.compact.max
54
+ end
55
+
56
+ def options_for source
57
+ heads = source.options.reject(&:tail?)
58
+ tails = (source.options - heads)
59
+
60
+ (heads + tails).map do |opt|
61
+ next if source != @global && opt.short == 'h'
62
+ line = padded(opt.short ? "-#{opt.short}," : "", 4)
63
+
64
+ if opt.long
65
+ value = opt.config[:argument] ? "#{opt.long.upcase}" : ""
66
+ value = "[#{value}]" if opt.accepts_optional_argument?
67
+ value = "=#{value}" unless value.empty?
68
+
69
+ line += "--#{opt.long}#{value}"
70
+ end
71
+
72
+ [Hobo.ui.color(line, :opt), opt.description]
73
+ end.compact
74
+ end
75
+
76
+ def commands_for source, opts = {}
77
+ source.commands.map do |name, command|
78
+ next if command.hidden && !opts[:all]
79
+ next unless command.hidden == false || command.description || opts[:all]
80
+ [Hobo.ui.color(name, :command), command.description]
81
+ end.compact
82
+ end
83
+
84
+ def usage source, command = nil
85
+ banner = source.banner
86
+ if banner.nil?
87
+ arg_list = (source.arg_list || []).map do |arg|
88
+ "<#{arg}>"
89
+ end
90
+
91
+ banner = "#{File.basename($0, '.*')}"
92
+ banner << " [command]" if source.commands.any? && command.nil?
93
+ banner << " #{command.split(':').join(' ')}" if command
94
+ banner << " #{arg_list.join(' ')}" if arg_list.size > 0
95
+ banner << " [options]"
96
+ end
97
+ end
98
+
99
+ def section title, contents, align_to = false
100
+ return nil if contents.empty?
101
+ output = Hobo.ui.color("#{title}:\n", :help_title)
102
+ output += contents.map do |line|
103
+ line.is_a?(String) ? " #{line}" : " #{padded(line[0], align_to)}#{line[1]}"
104
+ end.join("\n") + "\n"
105
+ end
106
+
107
+ def padded str, target
108
+ str + (' ' * (target - str.size))
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,39 @@
1
+ require 'open3'
2
+
3
+ module Hobo
4
+ module Helper
5
+ def locate(pattern, opts = {}, &block)
6
+ match = nil
7
+
8
+ Dir.chdir Hobo.project_path do
9
+ match = locate_git(pattern, false, &block)
10
+ match = locate_git(pattern, true, &block) if !match
11
+ end
12
+
13
+ return true if match
14
+
15
+ Hobo.ui.warning opts[:missing] if opts[:missing]
16
+ return false
17
+ end
18
+
19
+ private
20
+
21
+ def locate_git pattern, others, &block
22
+ args = [ 'git', 'ls-files', pattern ]
23
+ args.push '-o' if others
24
+ output = Hobo::Helper.shell *args, :capture => true
25
+ path = output.split("\n")[0]
26
+
27
+ unless path.nil?
28
+ path.strip!
29
+ Dir.chdir File.dirname(path) do
30
+ yield path
31
+ end
32
+
33
+ return true
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ include Hobo::Helper