nugrant 2.0.0.dev2 → 2.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +2 -1
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG.md +148 -3
  5. data/Gemfile +8 -20
  6. data/README.md +266 -72
  7. data/Rakefile +1 -0
  8. data/lib/nugrant.rb +14 -6
  9. data/lib/nugrant/bag.rb +116 -62
  10. data/lib/nugrant/helper/bag.rb +19 -19
  11. data/lib/nugrant/helper/env/exporter.rb +208 -0
  12. data/lib/nugrant/helper/env/namer.rb +47 -0
  13. data/lib/nugrant/helper/parameters.rb +12 -0
  14. data/lib/nugrant/helper/stack.rb +86 -0
  15. data/lib/nugrant/mixin/parameters.rb +98 -0
  16. data/lib/nugrant/parameters.rb +14 -68
  17. data/lib/nugrant/vagrant/errors.rb +27 -0
  18. data/lib/nugrant/vagrant/v2/command/env.rb +101 -0
  19. data/lib/nugrant/vagrant/v2/command/helper.rb +30 -0
  20. data/lib/nugrant/vagrant/v2/command/parameters.rb +16 -4
  21. data/lib/nugrant/vagrant/v2/command/restricted_keys.rb +60 -0
  22. data/lib/nugrant/vagrant/v2/command/root.rb +12 -2
  23. data/lib/nugrant/vagrant/v2/config/user.rb +9 -21
  24. data/lib/nugrant/vagrant/v2/plugin.rb +0 -1
  25. data/lib/nugrant/version.rb +1 -1
  26. data/locales/en.yml +13 -0
  27. data/nugrant.gemspec +3 -7
  28. data/test/lib/nugrant/helper/env/test_exporter.rb +238 -0
  29. data/test/lib/nugrant/helper/test_bag.rb +16 -0
  30. data/test/lib/nugrant/helper/test_parameters.rb +17 -0
  31. data/test/lib/nugrant/helper/test_stack.rb +152 -0
  32. data/test/lib/nugrant/test_bag.rb +132 -22
  33. data/test/lib/nugrant/test_config.rb +95 -92
  34. data/test/lib/nugrant/test_parameters.rb +232 -177
  35. data/test/lib/test_helper.rb +3 -0
  36. data/test/resources/json/params_user_nil_values.json +9 -0
  37. data/test/resources/vagrantfiles/v2.defaults_mixed_string_symbols +18 -0
  38. data/test/resources/vagrantfiles/v2.defaults_null_values_in_vagrantuser +23 -0
  39. data/test/resources/vagrantfiles/v2.defaults_using_string +18 -0
  40. data/test/resources/vagrantfiles/v2.defaults_using_symbol +18 -0
  41. data/test/resources/{Vagrantfile.v2.empty → vagrantfiles/v2.empty} +0 -2
  42. data/test/resources/{Vagrantfile.v2.fake → vagrantfiles/v2.fake} +4 -3
  43. data/test/resources/vagrantfiles/v2.missing_parameter +3 -0
  44. data/test/resources/{Vagrantfile.v2.real → vagrantfiles/v2.real} +0 -2
  45. data/test/resources/yaml/params_user_nil_values.yml +5 -0
  46. metadata +55 -88
  47. data/lib/nugrant/vagrant/v1/command/parameters.rb +0 -134
  48. data/lib/nugrant/vagrant/v1/command/root.rb +0 -81
  49. data/lib/nugrant/vagrant/v1/config/user.rb +0 -37
  50. data/lib/nugrant/vagrant/v1/plugin.rb +0 -6
  51. data/lib/vagrant_init.rb +0 -2
  52. data/test/resources/Vagrantfile.v1.empty +0 -2
  53. data/test/resources/Vagrantfile.v1.fake +0 -10
  54. data/test/resources/Vagrantfile.v1.real +0 -19
@@ -0,0 +1,47 @@
1
+ module Nugrant
2
+ module Helper
3
+ module Env
4
+ ##
5
+ # A namer is a lambda taking as argument an array of segments
6
+ # that should return a string representation of those segments.
7
+ # How the segments are transformed to a string is up to the
8
+ # namer. By using various namer, we can change how a bag key
9
+ # is transformed into and environment variable name. This is
10
+ # like the strategy pattern.
11
+ #
12
+ module Namer
13
+
14
+ ##
15
+ # Returns the default namer, which join segments together
16
+ # using a character and upcase the result.
17
+ #
18
+ # @param `char` The character used to join segments together, default to `"_"`.
19
+ #
20
+ # @return A lambda that will simply joins segment using the `char` argument
21
+ # and upcase the result.
22
+ #
23
+ def self.default(char = "_")
24
+ lambda do |segments|
25
+ segments.join(char).upcase()
26
+ end
27
+ end
28
+
29
+ ##
30
+ # Returns the prefix namer, which add a prefix to segments
31
+ # and delegate its work to another namer.
32
+ #
33
+ # @param prefix The prefix to add to segments.
34
+ # @param delegate_namer A namer that will be used to transform the prefixed segments.
35
+ #
36
+ # @return A lambda that will simply add prefix to segments and will call
37
+ # the delegate_namer with those new segments.
38
+ #
39
+ def self.prefix(prefix, delegate_namer)
40
+ lambda do |segments|
41
+ delegate_namer.call([prefix] + segments)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,12 @@
1
+ require 'nugrant/parameters'
2
+
3
+ module Nugrant
4
+ module Helper
5
+ module Parameters
6
+ def self.restricted_keys()
7
+ methods = Nugrant::Parameters.instance_methods() + Nugrant::Bag.instance_methods()
8
+ methods.uniq!
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,86 @@
1
+ module Nugrant
2
+ module Helper
3
+ class Stack
4
+ @@DEFAULT_MATCHER = /^(.+):([0-9]+)/
5
+
6
+ def self.fetch_error_region(stack, options = {})
7
+ entry = find_entry(stack, options)
8
+ location = extract_error_location(entry, options)
9
+
10
+ return (options[:unknown] || "Unknown") if not location[:file] and not location[:line]
11
+ return location[:file] if not location[:line]
12
+
13
+ fetch_error_region_from_location(location, options)
14
+ end
15
+
16
+ def self.fetch_error_region_from_location(location, options = {})
17
+ prefix = options[:prefix] || " "
18
+ width = options[:width] || 4
19
+ file = File.new(location[:file], "r")
20
+ line = location[:line]
21
+
22
+ index = 0
23
+
24
+ lines = []
25
+ while (line_string = file.gets())
26
+ index += 1
27
+ next if (line - index).abs > width
28
+
29
+ line_prefix = "#{prefix}#{index}:"
30
+ line_prefix += (line == index ? ">> " : " ")
31
+
32
+ lines << "#{line_prefix}#{line_string}"
33
+ end
34
+
35
+ lines.join().chomp()
36
+ rescue
37
+ return (options[:unknown] || "Unknown") if not location[:file] and not location[:line]
38
+ return location[:file] if not location[:line]
39
+
40
+ "#{location[:file]}:#{location[:line]}"
41
+ ensure
42
+ file.close() if file
43
+ end
44
+
45
+ ##
46
+ # Search a stack list (as simple string array) for the first
47
+ # entry that match the +:matcher+.
48
+ #
49
+ def self.find_entry(stack, options = {})
50
+ matcher = options[:matcher] || @@DEFAULT_MATCHER
51
+
52
+ stack.find do |entry|
53
+ entry =~ matcher
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Extract error location information from a stack entry using the
59
+ # matcher received in arguments.
60
+ #
61
+ # The usual stack entry format is:
62
+ # > /home/users/joe/work/lib/ruby.rb:4:Error message
63
+ #
64
+ # This function will extract the file and line information from
65
+ # the stack entry using the matcher. The matcher is expected to
66
+ # have two groups, the first for the file and the second for
67
+ # line.
68
+ #
69
+ # The results is returned in form of a hash with two keys, +:file+
70
+ # for the file information and +:line+ for the line information.
71
+ #
72
+ # If the matcher matched zero group, return +{:file => nil, :line => nil}+.
73
+ # If the matcher matched one group, return +{:file => file, :line => nil}+.
74
+ # If the matcher matched two groups, return +{:file => file, :line => line}+.
75
+ #
76
+ def self.extract_error_location(entry, options = {})
77
+ matcher = options[:matcher] || @@DEFAULT_MATCHER
78
+
79
+ result = matcher.match(entry)
80
+ captures = result ? result.captures : []
81
+
82
+ {:file => captures[0], :line => captures[1] ? captures[1].to_i() : nil}
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,98 @@
1
+ module Nugrant
2
+ module Mixin
3
+ ##
4
+ # Mixin module so it's possible to share parameters
5
+ # logic between default Parameters class and Vagrant
6
+ # implementation.
7
+ #
8
+ # This method delegates method missing to the overall
9
+ # bag instance. This means that even if the class
10
+ # including this module doesn't inherit Bag directly,
11
+ # it act exactly like one.
12
+ #
13
+ # To initialize the mixin module correctly, you must call
14
+ # the compute_bags! method at least once to initialize
15
+ # all variables. You should make this call in including
16
+ # class' constructor directly.
17
+ #
18
+ module Parameters
19
+ def method_missing(method, *args, &block)
20
+ case
21
+ when @__all.class.method_defined?(method)
22
+ @__all.send(method, *args, &block)
23
+ else
24
+ @__all[method]
25
+ end
26
+ end
27
+
28
+ def defaults()
29
+ @__defaults
30
+ end
31
+
32
+ ##
33
+ # Set the new default values for the
34
+ # various parameters contain by this instance.
35
+ # This will call __compute_all() to recompute
36
+ # correct precedences.
37
+ #
38
+ # =| Attributes
39
+ # * +elements+ - The new default elements
40
+ #
41
+ def defaults=(elements)
42
+ @__defaults = Bag.new(elements, @__options)
43
+
44
+ # When defaults change, we need to recompute parameters hierarchy
45
+ compute_all!(@__options)
46
+ end
47
+
48
+ ##
49
+ # Compute all parameters bags (current, user, system, default and all).
50
+ #
51
+ # =| Arguments
52
+ # * `config`
53
+ # The configuration object used to determine where to find the various
54
+ # bag source data. This can be either directly a `Nugrant::Config`
55
+ # object or a hash that will be pass to `Nugrant::Config` constructor.
56
+ #
57
+ # * `options`
58
+ # An options hash where some customization option can be passed.
59
+ # Defaults to an empty hash, see options for specific option default
60
+ # values.
61
+ #
62
+ # =| Options
63
+ # * `:defaults`
64
+ # A hash that is used as the initial data for the defaults bag. Defaults
65
+ # to an empty hash.
66
+ #
67
+ # * `:key_error`
68
+ # This option is passed to Bag.new constructor in it's options hash. See
69
+ # Bag.new for details on this options.
70
+ #
71
+ def compute_bags!(config, options = {})
72
+ config = config.kind_of?(Nugrant::Config) ? config : Nugrant::Config.new(config)
73
+
74
+ @__options = options
75
+
76
+ @__current = Helper::Bag.read(config.current_path, config.params_format, options)
77
+ @__user = Helper::Bag.read(config.user_path, config.params_format, options)
78
+ @__system = Helper::Bag.read(config.system_path, config.params_format, options)
79
+ @__defaults = Bag.new(options[:defaults] || {}, options)
80
+
81
+ compute_all!(options)
82
+ end
83
+
84
+ ##
85
+ # Recompute the correct precedences by merging the various
86
+ # bag in the right order and return the result as a Nugrant::Bag
87
+ # object.
88
+ #
89
+ def compute_all!(options = {})
90
+ @__all = Bag.new({}, options)
91
+ @__all.merge!(@__defaults)
92
+ @__all.merge!(@__system)
93
+ @__all.merge!(@__user)
94
+ @__all.merge!(@__current)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,87 +1,33 @@
1
1
  require 'nugrant/bag'
2
2
  require 'nugrant/config'
3
3
  require 'nugrant/helper/bag'
4
+ require 'nugrant/mixin/parameters'
4
5
 
5
6
  module Nugrant
6
7
  class Parameters
7
- attr_reader :__current, :__user, :__system, :__defaults, :__all
8
-
9
8
  ##
10
9
  # Create a new parameters object which holds completed
11
10
  # merged values. The following precedence is used to decide
12
11
  # which location has precedence over which location:
13
12
  #
14
13
  # (Highest) ------------------ (Lowest)
15
- # current < user < system < defaults
14
+ # project < user < system < defaults
16
15
  #
17
- # =| Options
18
- # * +:config+ - A hash that will be passed to Nugrant::Config.new().
19
- # See Nugrant::Config constructor for options that you can use.
20
- # * +:defaults+ - The default values for the various parameters that will be read. This
21
- # must be a Hash object.
16
+ # =| Arguments
17
+ # * `config`
18
+ # A hash that will be passed to Nugrant::Config.new() or
19
+ # a Nugrant::Config instance directly.
20
+ # See Nugrant::Config constructor for options that you can use.
22
21
  #
23
- def initialize(options = {})
24
- @__config = Config.new(options[:config])
25
-
26
- @__current = Helper::Bag.read(@__config.current_path, @__config.params_format)
27
- @__user = Helper::Bag.read(@__config.user_path, @__config.params_format)
28
- @__system = Helper::Bag.read(@__config.system_path, @__config.params_format)
29
- @__defaults = Bag.new(options[:defaults] || {})
30
-
31
- __compute_all()
32
- end
33
-
34
- def [](key)
35
- return @__all[key]
36
- end
37
-
38
- def method_missing(method, *args, &block)
39
- return @__all[method]
40
- end
41
-
42
- def empty?()
43
- @__all.empty?()
44
- end
45
-
46
- def has?(key)
47
- return @__all.has?(key)
48
- end
49
-
50
- def each(&block)
51
- @__all.each(&block)
52
- end
53
-
54
- ##
55
- # Set the new default values for the
56
- # various parameters contain by this instance.
57
- # This will call __compute_all() to recompute
58
- # correct precedences.
59
- #
60
- # =| Attributes
61
- # * +elements+ - The new default elements
22
+ # * `options`
23
+ # An options hash that is passed to Mixin::Parameters.compute_bags! method.
24
+ # See Mixin::Parameters.compute_bags! for details on the various options
25
+ # available.
62
26
  #
63
- def defaults=(elements)
64
- @__defaults = Bag.new(elements)
65
-
66
- # When defaults change, we need to recompute parameters hierarchy
67
- __compute_all()
27
+ def initialize(config, options = {})
28
+ compute_bags!(config, options)
68
29
  end
69
30
 
70
- ##
71
- # Recompute the correct precedences by merging the various
72
- # bag in the right order and return the result as a Nugrant::Bag
73
- # object.
74
- #
75
- def __compute_all()
76
- @__all = Bag.new()
77
- @__all.__merge!(@__defaults)
78
- @__all.__merge!(@__system)
79
- @__all.__merge!(@__user)
80
- @__all.__merge!(@__current)
81
- end
82
-
83
- def __to_hash()
84
- @__all.__to_hash()
85
- end
31
+ include Mixin::Parameters
86
32
  end
87
33
  end
@@ -0,0 +1,27 @@
1
+ require 'vagrant/errors'
2
+
3
+ require 'nugrant/helper/stack'
4
+
5
+ module Nugrant
6
+ module Vagrant
7
+ module Errors
8
+ class NugrantVagrantError < ::Vagrant::Errors::VagrantError
9
+ error_namespace("nugrant.vagrant.errors")
10
+ end
11
+
12
+ class ParameterNotFoundError < NugrantVagrantError
13
+ error_key(:parameter_not_found)
14
+
15
+ def initialize(options = nil, *args)
16
+ super({:context => compute_context()}.merge(options || {}), *args)
17
+ end
18
+
19
+ def compute_context()
20
+ Helper::Stack.fetch_error_region(caller(), {
21
+ :matcher => /(.+Vagrantfile):([0-9]+)/
22
+ })
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,101 @@
1
+ require 'nugrant'
2
+ require 'nugrant/helper/env/exporter'
3
+ require 'nugrant/parameters'
4
+
5
+ EnvExporter = Nugrant::Helper::Env::Exporter
6
+
7
+ module Nugrant
8
+ module Vagrant
9
+ module V2
10
+ module Command
11
+ class Env < ::Vagrant.plugin("2", :command)
12
+ def initialize(arguments, environment)
13
+ super(arguments, environment)
14
+
15
+ @unset = false
16
+ @format = :terminal
17
+ @show_help = false
18
+ end
19
+
20
+ def create_parser()
21
+ return OptionParser.new do |parser|
22
+ parser.banner = "Usage: vagrant user env [<options>]"
23
+ parser.separator ""
24
+
25
+ parser.separator "Outputs the commands that should be executed to export\n" +
26
+ "the various parameter as environment variables. By default,\n" +
27
+ "existing ones are overridden. The --format argument can be used\n" +
28
+ "to choose in which format the variables should be displayed.\n" +
29
+ "Changing the format will also change where they are displayed.\n"
30
+ parser.separator ""
31
+
32
+ parser.separator "Available formats:"
33
+ parser.separator " autoenv => Write commands to a file named `.env` in the current directory.\n" +
34
+ " See https://github.com/kennethreitz/autoenv for more info."
35
+ parser.separator " terminal => Display commands to terminal so they can be sourced."
36
+ parser.separator " script => Write commands to a bash script named `nugrant2env.sh` so it can be sourced."
37
+ parser.separator ""
38
+
39
+ parser.separator "Available options:"
40
+ parser.separator ""
41
+
42
+ parser.on("-u", "--[no-]unset", "Generates commands needed to unset environment variables, default false") do |unset|
43
+ @unset = unset
44
+ end
45
+
46
+ parser.on("-f", "--format FORMAT", "Determines in what format variables are output, default to terminal") do |format|
47
+ @format = format.to_sym()
48
+ end
49
+
50
+ parser.on("-h", "--help", "Print this help") do
51
+ @show_help = true
52
+ end
53
+ end
54
+ end
55
+
56
+ def error(message, parser)
57
+ @env.ui.info("ERROR: #{message}", :prefix => false)
58
+ @env.ui.info("", :prefix => false)
59
+
60
+ help(parser)
61
+
62
+ return 1
63
+ end
64
+
65
+ def help(parser)
66
+ @env.ui.info(parser.help, :prefix => false)
67
+ end
68
+
69
+ def execute
70
+ parser = create_parser()
71
+ arguments = parse_options(parser)
72
+
73
+ return error("Invalid format value '#{@format}'", parser) if not EnvExporter.valid?(@format)
74
+ return help(parser) if @show_help
75
+
76
+ @logger.debug("Nugrant 'Env'")
77
+ with_target_vms(arguments) do |vm|
78
+ config = vm.config.user
79
+ parameters = config ? config : Nugrant::Parameters.new()
80
+ bag = parameters.__all
81
+
82
+ options = {:type => @unset ? :unset : :export}
83
+
84
+ case
85
+ when @format == :script
86
+ EnvExporter.script_exporter(bag, options)
87
+ when @format == :autoenv
88
+ EnvExporter.autoenv_exporter(bag, options)
89
+ when @format == :terminal
90
+ EnvExporter.terminal_exporter(bag, options)
91
+ end
92
+
93
+ # No need to execute for the other VMs
94
+ return 0
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end