nugrant 2.0.0.dev2 → 2.0.0.pre1

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