├── .buildpath ├── .gitattributes ├── .gitignore ├── .project ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTORS.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── nugrant.rb └── nugrant │ ├── bag.rb │ ├── config.rb │ ├── helper │ ├── bag.rb │ ├── env │ │ ├── exporter.rb │ │ └── namer.rb │ ├── parameters.rb │ ├── stack.rb │ └── yaml.rb │ ├── mixin │ └── parameters.rb │ ├── parameters.rb │ ├── vagrant │ ├── errors.rb │ └── v2 │ │ ├── action.rb │ │ ├── action │ │ └── auto_export.rb │ │ ├── command │ │ ├── env.rb │ │ ├── parameters.rb │ │ ├── restricted_keys.rb │ │ └── root.rb │ │ ├── config │ │ └── user.rb │ │ ├── helper.rb │ │ └── plugin.rb │ └── version.rb ├── locales ├── en.yml └── fr.yml ├── nugrant.gemspec └── test ├── lib ├── nugrant │ ├── helper │ │ ├── env │ │ │ └── test_exporter.rb │ │ ├── test_bag.rb │ │ ├── test_parameters.rb │ │ └── test_stack.rb │ ├── test_bag.rb │ ├── test_config.rb │ └── test_parameters.rb └── test_helper.rb └── resources ├── .vagrantuser ├── README.md ├── json ├── params_array.json ├── params_boolean.json ├── params_combinations.json ├── params_current_1.json ├── params_current_2.json ├── params_defaults_at_root.json ├── params_defaults_not_at_root.json ├── params_empty.json ├── params_list.json ├── params_simple.json ├── params_system_1.json ├── params_system_2.json ├── params_unix_eol.json ├── params_user_1.json ├── params_user_2.json ├── params_user_nil_values.json └── params_windows_eol.json ├── vagrantfiles ├── v2.auto_export ├── v2.bag_inside_array ├── v2.defaults_mixed_string_symbols ├── v2.defaults_null_values_in_vagrantuser ├── v2.defaults_using_string ├── v2.defaults_using_symbol ├── v2.empty ├── v2.fake ├── v2.missing_parameter └── v2.real └── yaml ├── params_array.yml ├── params_boolean.yml ├── params_combinations.yml ├── params_current_1.yml ├── params_current_2.yml ├── params_defaults_at_root.yml ├── params_defaults_not_at_root.yml ├── params_empty.yml ├── params_list.yml ├── params_numeric_key.yml ├── params_simple.yml ├── params_system_1.yml ├── params_system_2.yml ├── params_unix_eol.yml ├── params_user_1.yml ├── params_user_2.yml ├── params_user_nil_values.yml └── params_windows_eol.yml /.buildpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Handle line endings automatically for files detected as text 2 | # and leave all files detected as binary untouched. 3 | * text=auto 4 | 5 | # 6 | # The above will handle all files NOT found below 7 | # 8 | # These files are text and should be normalized (Convert crlf => lf) 9 | Gemfile 10 | Rakefile 11 | Vagrantfile 12 | *.rb text 13 | *.md text 14 | *.gemspec text 15 | *.lock text 16 | *.txt text 17 | *.yml text 18 | *.yaml text 19 | *.prefs text 20 | 21 | # Eclipse specific files 22 | *.project text 23 | *.buildpath text 24 | 25 | # Those have specific eol for testing purpose 26 | test/resources/json/params_unix_eol.json eol=lf 27 | test/resources/json/params_windows_eol.json eol=crlf 28 | test/resources/yml/params_unix_eol.yml eol=lf 29 | test/resources/yml/params_windows_eol.yml eol=crlf 30 | 31 | # These files are binary and should be left untouched 32 | # (binary is a macro for -text -diff) 33 | *.gem binary 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | .vagrant 7 | Gemfile.lock 8 | InstalledFiles 9 | _yardoc 10 | coverage 11 | doc/ 12 | lib/nugrant_debug.rb 13 | lib/bundler/man 14 | pkg 15 | rdoc 16 | spec/reports 17 | test/tmp 18 | test/version_tmp 19 | tmp 20 | .idea 21 | 22 | test/resources/vagrantfiles/Vagrantfile 23 | test/resources/vagrantfiles/.vagrantuser 24 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | nugrant 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.dltk.core.scriptbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.dltk.ruby.core.nature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 4 | - 2.0.0 5 | - 2.1 6 | - 2.2.3 7 | 8 | before_install: 9 | - rvm @global do gem uninstall bundler -a -x 10 | - rvm @global do gem install bundler -v 1.10.6 11 | 12 | install: 13 | - bundle install --without doc guard --jobs 3 --retry 3 14 | 15 | script: bundle exec rake 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.1.5 (In Progress) 2 | 3 | # 2.1.4 (November 12th, 2024) 4 | 5 | * Remove usage of `File.exists?` that has been removed and replace with `File.exist?`. 6 | 7 | # 2.1.3 (February 24th, 2016) 8 | 9 | * Ensures Unix line ending are used when generating env scripts 10 | (`autoenv` and `bash`). 11 | 12 | # 2.1.2 (January 12th, 2015) 13 | 14 | * Fixed indifferent access inside arrays. Array elements of type `Hash` 15 | are now converted to `Bag` instances (recursively). This fix the 16 | indifferent access of `Bag` elements inside arrays. 17 | 18 | Fixes [issue #27](https://github.com/maoueh/nugrant/issues/27). 19 | 20 | # 2.1.1 (December 2nd, 2014) 21 | 22 | * Permit numeric keys in bag. They are converted to symbol 23 | like others. 24 | 25 | Fixes [issue #26](https://github.com/maoueh/nugrant/issues/26). 26 | 27 | * Removed old code that was switching YAML engine to `syck` when 28 | it was available. 29 | 30 | Fixes [issue #14](https://github.com/maoueh/nugrant/issues/14) again. 31 | 32 | * Fixed auto export variables on `vagrant provision` feature. The 33 | initial release is not working correctly. 34 | 35 | * Changed how module shortcut is defined. The shortcut is now defined 36 | inside the class using it to avoid already defined warnings. 37 | 38 | Fixes [issue #24](https://github.com/maoueh/nugrant/issues/24). 39 | 40 | # 2.1.0 (November 1st, 2014) 41 | 42 | * Added possibility to specify the script path where to generate 43 | the environment commands (export/unset) when using the 44 | `vagrant user env` command. 45 | 46 | * Added possibility to automatically export variables on vagrant provision. 47 | This can be used by specifying `config.user.auto_export = ` in 48 | your Vagrantfile where can be one of: 49 | 50 | * `false` => No auto export (default value). 51 | * `:autoenv` => Export to [autoenv](https://github.com/kennethreitz/autoenv) script format. 52 | * `:script` => Export to a bash script file. 53 | * `[:autoenv, :script]` => export both format. 54 | 55 | The default generated script path is "./nugrant2env.sh". You can change 56 | the default script name by specifying the configuration key `config.user.auto_export_script_path` 57 | in your Vagrantfile: 58 | 59 | config.user.auto_export_script_path = "./script/example.sh" 60 | 61 | Contributed by [@ruifil](https://github.com/ruifil). 62 | 63 | # 2.0.2 (July 13th, 2014) 64 | 65 | * Fixed a bug when using some vagrant commands. The code to retrieve 66 | the Vagrantfile name was not accurate in respect to where it was 67 | copied. When the env variable `VAGRANT_VAGRANTFILE` is set, it 68 | then must be wrapped inside an array. 69 | 70 | Fixes [issue #21](https://github.com/maoueh/nugrant/issues/21). 71 | 72 | # 2.0.1 (July 10th, 2014) 73 | 74 | * Fixed a bug when using the plugin. A require clause was missing, 75 | it was working when doing commands but not when using Vagrant 76 | directly. 77 | 78 | Fixes [issue #20](https://github.com/maoueh/nugrant/issues/20). 79 | 80 | # 2.0.0 (July 9th, 2014) 81 | 82 | * Fixed retrieval of current directory for `.vagrantuser`. The directory 83 | is now that same as the one of the `Vagrantfile`. Rules have been 84 | copied for Vagrant's one, hence, the behavior should be the same. 85 | 86 | * Fixed bad implementation of config class `::Vagrant.plugin("2", :config)` 87 | where `merge` was not implemented and was causing errors. Now, all objects 88 | (i.e. `Config`, `Bag` and `Parameters` implements `merge` and `merge!` 89 | correctly). 90 | 91 | * Added possibility to change array merge strategy. This can 92 | be used in Vagrant by doing `config.user.array_merge_strategy = ` 93 | where valid strategies are: 94 | 95 | * :replace => Replace current values by new ones 96 | * :extend => Merge current values with new ones 97 | * :concat => Append new values to current ones 98 | 99 | * Better handling in Vagrant of cases where the vagrant user 100 | file cannot be parsed correctly. This is now reported 101 | as an error in Vagrant an nicely displayed with the path 102 | of the offending file and the parser error message. 103 | 104 | * Better handling of how global Nugrant options are passed and 105 | handled. Everything is now located in the `Nugrant::Config` 106 | object and used by everything that need some configuration 107 | parameters. 108 | 109 | * It is now possible to customize key error handling by passing 110 | an options hash with key `:key_error` and a `Proc` value. 111 | 112 | * Improved command `vagrant user parameters`. The command now checks if 113 | restricted keys are used and prints a warning when it's the case. 114 | 115 | * Added a new command `vagrant user restricted-keys` that prints the keys that 116 | are restricted, i.e. that cannot be accessed using method access 117 | syntax. 118 | 119 | * Added possibility to specify merge strategy to use when merging 120 | two arrays together. 121 | 122 | ### Backward Incompatibilities 123 | 124 | * Removed deprecated `--script` argument from `vagrant user env` command. 125 | 126 | * Support for Ruby <= 1.9.2 has been dropped. This is not a problem when using 127 | Nugrant as a Vagrant plugin. Use branch `1.x` if you can't upgrade to 128 | Ruby >= 1.9.3. 129 | 130 | * Support for Vagrant 0.x has been dropped. This means that Nugrant 2.x will not 131 | load if installed in a Vagrant 0.x environment. Use branch `1.x` if you can't 132 | upgrade to Vagrant 1.x. 133 | 134 | * `Bag` and `Parameters` and Vagrant configuration object `config.user` are now 135 | [Enumerable](http://ruby-doc.org/core-2.0.0/Enumerable.html). 136 | 137 | This change has implications on the resolving process of the variables 138 | that are stored in the `Bag` when using the dot syntax `(config.user.email)` 139 | in your code and `Vagrantfiles`. By using this syntax with version 2.0, some keys 140 | will collapse with the internal object's methods. In fact, it was already the 141 | case before but to a much smaller scope because object were not enumerable. 142 | 143 | The number of conflicts should be rather low because the restricted keys 144 | are not commonly used as parameter name. The list of the restricted keys 145 | is the following: 146 | 147 | !, !=, !~, <=>, ==, ===, =~, [], __all, __current, __defaults, 148 | __id__, __send__, __system, __user, _detected_errors, _finalize!, 149 | all?, any?, chunk, class, clear!, clone, collect, collect_concat, 150 | compute_all!, compute_bags!, count, cycle, defaults, defaults=, 151 | define_singleton_method, detect, display, drop, drop_while, dup, 152 | each, each_cons, each_entry, each_slice, each_with_index, 153 | each_with_object, empty?, entries, enum_for, eql?, equal?, extend, 154 | finalize!, find, find_all, find_index, first, flat_map, freeze, 155 | frozen?, gem, grep, group_by, has?, hash, include?, inject, 156 | inspect, instance_eval, instance_exec, instance_of?, 157 | instance_variable_defined?, instance_variable_get, 158 | instance_variable_set, instance_variables, instance_variables_hash, 159 | is_a?, kind_of?, lazy, map, max, max_by, member?, merge, merge!, 160 | method, method_missing, methods, min, min_by, minmax, minmax_by, 161 | nil?, none?, object_id, one?, partition, private_methods, 162 | protected_methods, psych_to_yaml, public_method, public_methods, 163 | public_send, reduce, reject, remove_instance_variable, respond_to?, 164 | reverse_each, select, send, set_options, singleton_class, 165 | singleton_methods, slice_before, sort, sort_by, suppress_warnings, 166 | taint, tainted?, take, take_while, tap, to_a, to_enum, to_hash, 167 | to_json, to_s, to_set, to_yaml, to_yaml_properties, trust, untaint, 168 | untrust, untrusted?, validate, zip 169 | 170 | * The `Parameter` class has a new API. 171 | 172 | * The `Bag` class has a new API. 173 | 174 | # 1.4.2 (January 11th, 2014) 175 | 176 | * Fixed Vagrant `user` config class to make the `has?` method 177 | available to people using Vagrant. This considered has a bug 178 | fix because using `has?` was not working anyway before. 179 | 180 | # 1.4.1 (December 15th, 2013) 181 | 182 | * Fixed a superfluous warning message when using ruby >= 2.0.0 which is now the 183 | default when installing Vagrant >= 1.4.0 (at least on Windows). 184 | 185 | # 1.4.0 (November 28th, 2013) 186 | 187 | * Adding support to export to an [autoenv](https://github.com/kennethreitz/autoenv) 188 | file. See [GH-13](https://github.com/maoueh/nugrant/issues/13). 189 | 190 | * Deprecated usage of `-s, --script` option for command 191 | `vagrant user env`. This was replaced by the more generic 192 | and extensible `-f, --format FORMAT` option. The 193 | `-s, --script` option will be removed in 2.0. 194 | 195 | # 1.3.0 (November 19th, 2013) 196 | 197 | * Now using [minitest](https://github.com/seattlerb/minitest) as our 198 | testing library. 199 | 200 | * Added a new command that can be used either standalone or via 201 | a small bash script to easily export environment variables 202 | from your currently set parameters. See 203 | [GH-13](https://github.com/maoueh/nugrant/issues/13). 204 | 205 | * Keys associated to a null value are considered as being missing 206 | by the merge process. It is still possible to define a null 207 | parameter, but it will be overridden by any parameter and will not 208 | override any. See [GH-12](https://github.com/maoueh/nugrant/issues/12). 209 | 210 | * Fixed output of command `vagrant user parameters`, the keys were 211 | serialized as symbol instead of string. 212 | 213 | # 1.2.0 (October 24th, 2013) 214 | 215 | * Now showing better error message to the end-user when a parameter 216 | cannot be found. The message displays which key could not be found. 217 | Moreover, we show the context within the Vagrantfile where we think 218 | the error occurred: 219 | 220 | ``` 221 | Nugrant: Parameter 'param' was not found, is it defined in 222 | your .vagrantuser file? Here where we think the error 223 | could be in your Vagrantfile: 224 | 225 | 1: Vagrant.configure("2") do |config| 226 | 2:>> puts config.user.param 227 | 3: end 228 | ``` 229 | 230 | See [GH-8] (https://github.com/maoueh/nugrant/issues/8). 231 | 232 | * Ensured that keys used within a `Bag` are always symbol. This make 233 | sure that it is possible to retrieve a value with any access method. 234 | See [GH-9](https://github.com/maoueh/nugrant/issues/9). 235 | 236 | * Now using [multi_json](https://rubygems.org/gems/multi_json) 237 | for JSON handling. 238 | 239 | # 1.1.0 (May 17th, 2013) 240 | 241 | * Rewrite completely classes `Parameters` and `Bag`. 242 | * Reduced chances to have a parameter name collapsing with an 243 | implementation method. 244 | 245 | * Removed dependency on `deep_merge`. We do now perform 246 | our own merge. 247 | 248 | * Added possibility to iterate through keys by using 249 | `.each`: 250 | 251 | ``` 252 | config.user.local.each do |name, value| 253 | puts "Key #{name}: #{value}" 254 | end 255 | ``` 256 | 257 | ### Backward Incompatibilities 258 | 259 | * `Parameters` is not extending the `Bag` class anymore. 260 | 261 | * `Parameters` and `Bag` attributes and methods are now almost 262 | all prefixed with __ to reduce clashes to a minimum when 263 | accessing parameters with method-like syntax 264 | (i.e. `parameters.git.master` instead of `parameters['git']['master']`) 265 | 266 | # 1.0.1 (April 9th, 2013) 267 | 268 | * Fixed a crash when `user` config value is `nil` preventing `vagrant user parameters` 269 | from working as expected. See [GH-4](https://github.com/maoueh/nugrant/issues/4). 270 | 271 | * Fixed a bug preventing the version from being printed when doing `vagrant user -v`. 272 | 273 | # 1.0.0 (March 21th, 2013) 274 | 275 | * For now on, this gem will follow semantic versioning. 276 | * Refactored how Vagrant plugin is architectured. 277 | * Now supporting Vagrant 1.1.x (Plugin version "2"). 278 | 279 | # 0.0.14 280 | 281 | * Renamed `ParameterBag` to `Bag` 282 | 283 | * Cleanup `Bag` api 284 | * Renamed method `has_param?` to `has_key?` in `Bag` 285 | * Removed method `get_params` from `Bag` 286 | 287 | # 0.0.13 288 | 289 | * Cleanup `Parameters` and `ParameterBag` interface 290 | * The method `defaults` has been removed from the bag 291 | * Setting defaults on `Parameters` now recompute the final bag 292 | 293 | * Improved `vagrant user parameters` command 294 | * Now using the exact config as seen by Vagrant, this includes defaults parameters 295 | * An option has been added to only see defaults parameters 296 | 297 | # 0.0.12 298 | 299 | * Added travis configuration file 300 | 301 | * Added travis build status icon to readme 302 | 303 | * Fixed a bug when `.vagrantuser` file is empty or not a hash type 304 | 305 | * Improved parameters command 306 | * The parameters command is now a proper subcommand 307 | * An option has been added to see system parameters 308 | * An option has been added to see user parameters 309 | * An option has been added to see project parameters 310 | 311 | # 0.0.11 312 | 313 | * Updated README file for installation via rubygems.org 314 | 315 | # 0.0.10 316 | 317 | * Added a subcommand `parameters` for vagrant command `user` 318 | 319 | * Added a vagrant command `vagrant user subcommand [options]` 320 | 321 | # 0.0.9 322 | 323 | * Fixed a bug with the new default values implementation 324 | 325 | # 0.0.8 326 | 327 | * Introduced possibility to set default values 328 | 329 | * Introduced restricted keys (For now, restricted keys are [`defaults`]). 330 | 331 | * Fixed a bug with system-wide parameters 332 | 333 | # 0.0.7 334 | 335 | * YAML is back as the default file format for parameters 336 | 337 | # 0.0.6 338 | 339 | * Fixed a bug on ruby 1.8.7 which doesn't have yaml included in its load path by default 340 | 341 | # 0.0.5 342 | 343 | * Introduced system-wide parameters file 344 | 345 | # 0.0.4 346 | 347 | * JSON is now the default file format for parameters (due to problem with YAML) 348 | 349 | * It is now possible to store parameters in the JSON format 350 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | Thanks for this people who contributed some code to Nugrant: 4 | 5 | * Rui Filipe Da Cunha Alves ([@ruifil](https://github.com/ruifil)) 6 | * Michał Sochoń ([@nvtkaszpir](https://github.com/nvtkaszpir)) 7 | * jpegger ([@jpegger](https://github.com/jpegger)) 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :development do 4 | gem "rake", "~> 10.3" 5 | gem "minitest", "~> 5.3" 6 | gem "minitest-reporters", "~> 1.0" 7 | 8 | gem "vagrant", :git => "https://github.com/mitchellh/vagrant.git", :branch => "master" 9 | end 10 | 11 | group :plugins do 12 | gem "nugrant", path: "." 13 | end 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014 Matthieu Vachon 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nugrant 2 | 3 | [![Gem Version](https://badge.fury.io/rb/nugrant.png)][gem] 4 | [![Build Status](https://secure.travis-ci.org/maoueh/nugrant.png?branch=master)][travis] 5 | [![Dependency Status](https://gemnasium.com/maoueh/nugrant.png?travis)][gemnasium] 6 | [![Code Climate](https://codeclimate.com/github/maoueh/nugrant.png)][codeclimate] 7 | 8 | [gem]: https://rubygems.org/gems/nugrant 9 | [travis]: http://travis-ci.org/maoueh/nugrant 10 | [gemnasium]: https://gemnasium.com/maoueh/nugrant 11 | [codeclimate]: https://codeclimate.com/github/maoueh/nugrant 12 | 13 | Nugrant is a library to easily handle parameters that need to be 14 | injected into an application via different sources (system, user, 15 | current, defaults). But foremost, a Vagrant plug-in that will enhance 16 | Vagrantfile to allow user specific configuration values. 17 | 18 | Supported platforms: 19 | 20 | * Vagrant 1.x 21 | * Ruby 1.9.3+ 22 | 23 | ## Quick Start 24 | 25 | Using Nugrant as a plug-in provides an easy and hierarchical system to manage 26 | machine and user specific parameters. 27 | 28 | Let's start with an example. You need to distribute among your enterprise a 29 | `Vagrantfile` to start and provision an AWS EC2 instance (or for an open-source project). 30 | The `aws_access_key` and `aws_secret_key` should be configurable depending on the user 31 | running `vagrant up`. 32 | 33 | To achieve this, simply create a file named `.vagrantuser` that resides in the directory 34 | as your `Vagrantfile`: 35 | 36 | aws: 37 | access_key: "123456" 38 | secret_key: "abcdef" 39 | 40 | In your `Vagrantfile`, `Nugrant` will let you access the parameters via the 41 | `config.user` object: 42 | 43 | Vagrant.configure("2") do |config| 44 | ... 45 | 46 | config.vm.provider :aws do |aws, override| 47 | aws.access_key_id = config.user.aws.access_key 48 | aws.secret_access_key = config.user.aws.secret_key 49 | 50 | ... 51 | end 52 | end 53 | 54 | You then ignore the `.vagrantuser` file in your revision control, so each developer 55 | as a specific one with their own values. People often commit a `.vagrantuser.example` 56 | file in project's repository as an easy startup for the various parameters that 57 | must be filled in, something like: 58 | 59 | aws: 60 | access_key: "" 61 | secret_key: "" 62 | 63 | To find where your project `.vagrantuser` is located, Nugrant uses the directory 64 | where the `Vagrantfile` is located. It achieves this using the same set of 65 | rules as Vagrant meaning you can be in a nested directory and parameters 66 | will still be fetched correctly. 67 | 68 | Moreover, like other similar system, there is a hierarchy of `.vagrantuser` files 69 | that you can leverage: 70 | 71 | | Name | Location | Priority | Overrides | 72 | | ---------|-----------------------------------------|:---------:|--------------------------| 73 | | Defaults | [config.user.defaults](#default-values) | 4 | - | 74 | | System | $SYSTEM/.vagrantuser | 3 | Defaults | 75 | | User | ~/.vagrantuser | 2 | Defaults & System | 76 | | Project | .vagrantuser | 1 | Defaults & System & User | 77 | 78 | The project level directory is always the same as the directory where your 79 | `Vagrantfile` resides and same rules as Vagrant are used to find it. 80 | The `~` is the user's home directory and `$SYSTEM` is the platform dependent 81 | folder where system global can be put. Check [Hierarchy](#hierarchy) section 82 | for where `$SYSTEM` maps exactly. 83 | 84 | You can use the command `vagrant user parameters` to see the final merged 85 | hierarchy seen by Nugrant. This command also prints [restricted keys](#restricted-keys) 86 | defined in your hierarchy. 87 | 88 | Accessing parameters in your `Vagrantfile` can be done either by method access 89 | (i.e. `config.user.`) or by array access (i.e. `config.user[]`). 90 | This support is working for any deepness, only `config.user` is different 91 | because provided directly by `Vagrant` and not by this plugin. 92 | 93 | However, a drawback with method access, not present with array access, is its 94 | set of [restricted keys](#restricted-keys) for which usage is prohibited. These 95 | are in facts calls to method defined by the [Bag](lib/nugrant/bag.rb) class 96 | ([Bag](lib/nugrant/bag.rb) extends [Hash](http://ruby-doc.org/core-2.0/Hash.html)). 97 | It's plain Ruby, use it at your advantage like iterating through a collection 98 | using the `.each` method. 99 | 100 | This is where the quick start end. Continue to section [Installation](#installation) 101 | if you need so help on how to install Nugrant. Or jump to [Usage](#usage) section 102 | which describe in greater details all necessary information needed to deeply 103 | understand Nugrant and use it at its full potential. 104 | 105 | ## Installation 106 | 107 | ### Vagrant 108 | 109 | Vagrant's plug-in system is very well done and Nugrant supports 110 | the following plug-in API versions: 111 | 112 | * V2 => Vagrant 1.x 113 | 114 | To install the Nugrant as a Vagrant plug-in, simply type the following 115 | command in a terminal: 116 | 117 | vagrant plugin install nugrant 118 | 119 | #### Vagrant 0.x 120 | 121 | Vagrant 0.x is not supported anymore. If you still need support for 122 | Vagrant 0.x, please use release line `1.x` (branch [1.x](https://github.com/maoueh/nugrant/tree/1.x)). 123 | 124 | ### Library 125 | 126 | If you would like to use Nugrant as a library, simply reference 127 | it as a dependency of your application. Probably by adding it to 128 | your `Gemfile` or your `.gemspec` file. 129 | 130 | "nugrant", "~> 2.0" 131 | 132 | ## Usage 133 | 134 | Whether used as a library or a Vagrant plug-in, Nugrant has some 135 | common concepts that apply to both usages. The most important 136 | one is the parameters hierarchy. 137 | 138 | ### Common 139 | 140 | Nugrant can read parameters from various locations and will merge 141 | them all together in a single set. Merging is done in a fairly 142 | standard fashion. 143 | 144 | Here the precedence rules that apply when merging parameters 145 | from various location. List index indicate the priority of the 146 | entry. Entry with lower number has lower priority (values at this 147 | priority will be overridden by values defined on higher priorities). 148 | 149 | 1. Defaults 150 | 2. System 151 | 3. User 152 | 4. Current 153 | 154 | In text, this means that current parameters overrides user 155 | parameters, user parameters overrides system parameters and 156 | finally system parameters overrides defaults parameters. 157 | 158 | When two keys that are merged together points to Array values, 159 | the default operation is to replace current Array by 160 | overriding one. The default merge strategy can be customized. 161 | 162 | ### Vagrant 163 | 164 | All examples shown here are for Vagrant 1.1+. They have 165 | been tested with Vagrant 1.4.0. Keep this in mind when 166 | copying examples. 167 | 168 | Let start with a small use case. Say the git repository you want 169 | to share with your guest VM is not located under the root folder of 170 | your `Vagrantfile`. That means you will need to specify 171 | an absolute host path to share the folder on the guest VM. 172 | 173 | Your `Vagrantfile` would look like this: 174 | 175 | Vagrant.configure("2") do |config| 176 | config.vm.box = "base" 177 | config.vm.synced_folder "/home/user/work/git", "/git" 178 | end 179 | 180 | However, what happens when multiple developers 181 | need to share the same `Vagrantfile`? This is the main 182 | use case this plug-in try to address. 183 | 184 | When Vagrant starts, it loads all vagrant plug-ins it knows 185 | about. If you installed the plug-in with one of the two 186 | methods we listed above, Vagrant will know about Nugrant 187 | and will load it correctly. 188 | 189 | To use the plug-in, first create a YAML file named 190 | `.vagrantuser` in the same folder where your `Vagrantfile` is 191 | located. The file must be a valid YAML file: 192 | 193 | repository: 194 | project: "/home/user/work/git" 195 | 196 | The configuration hierarchy you define in the `.vagrantuser` file 197 | is imported into the `config` object of the `Vagrantfile` 198 | under the key `user`. So, with the `.vagrantuser` file above, you 199 | could have this `Vagrantfile` that abstract absolute paths. 200 | 201 | Vagrant.configure("2") do |config| 202 | config.vm.box = "base" 203 | config.vm.synced_folder config.user.repository.project, "/git" 204 | end 205 | 206 | This way, paths can be customized by every developer. They just 207 | have to add a `.vagrantuser` file where user specific configuration 208 | values can be specified. The `.vagrantuser` should be ignored by you 209 | version control system so it is to committed with the project. 210 | 211 | Additionally, you can also have a `.vagrantuser` under your user home 212 | directory. This way, you can set parameters that will be 213 | available to all your `Vagrantfile` files. The `.vagrantuser` located 214 | within the same folder as the `Vagrantfile` file will overrides 215 | parameters defined in the `.vagrantuser` file defined in the user 216 | home directory. 217 | 218 | For example, you have `.vagrantuser` file located at `~/.vagrantuser` 219 | that has the following content: 220 | 221 | ssh_port: 2223 222 | repository: 223 | project: "/home/user/work/git" 224 | 225 | And another `.vagrantuser` within the same folder as your `Vagrantfile`: 226 | 227 | ssh_port: 3332 228 | repository: 229 | personal: "/home/user/personal/git" 230 | 231 | Then, the `Vagrantfile` could be defined like this: 232 | 233 | Vagrant.configure("2") do |config| 234 | config.ssh.port config.user.ssh_port 235 | 236 | config.vm.synced_folder config.user.repository.project, "/git" 237 | config.vm.synced_folder config.user.repository.personal, "/personal" 238 | end 239 | 240 | That would be equivalent to: 241 | 242 | Vagrant.configure("2") do |config| 243 | config.ssh.port 3332 244 | 245 | config.vm.synced_folder "/home/user/work/git", "/git" 246 | config.vm.synced_folder "/home/user/personal/git", "/personal" 247 | end 248 | 249 | #### Hierarchy 250 | 251 | As you can see, the parameters defined in the second `.vagrantuser` file 252 | (the current one) overrides settings defined in the `.vagrantuser` found 253 | in the home directory (the user one). 254 | 255 | Here the list of locations where Nugrant looks for parameters: 256 | 257 | 1. Defaults (via `config.user.defaults` in `Vagrantfile`) 258 | 2. System (`/etc/.vagrantuser` on Unix, `%PROGRAMDATA%/.vagrantuser` or `%ALLUSERSPROFILE%/.vagrantuser` on Windows) 259 | 3. Home (`~/.vagrantuser`) 260 | 4. Project (`.vagrantuser` within the same folder as the `Vagrantfile`) 261 | 262 | #### Paths 263 | 264 | When you want to specify paths on, specially on Windows, it's probably 265 | better to only use forward slash (`/`). The main reason for this is because 266 | Ruby, which will be used at the end by Vagrant is able to deal with forward 267 | slash even on Windows. This is great because with this, you can avoid 268 | values escaping in YAML file. If you need to use backward slash (`\`), don't 269 | forget to properly escape it! 270 | 271 | value: "C:/Users/user/work/git" 272 | value: "C:\\Users\\user\\work\\git" 273 | 274 | Moreover, it is preferable that paths are specified in full 275 | (i.e. no `~` for HOME directory for example). Normally, they 276 | should be handled by `Vagrant` but it may happen that it's not 277 | the case. If your have an error with a specific parameter, 278 | either expand it in your config: 279 | 280 | project: "/home/joe/work/ruby/git" 281 | 282 | Of expand it in the `Vagrantfile`: 283 | 284 | config.vm.synced_folder File.expand_path(config.user.repository.project), "/git" 285 | 286 | #### Parameters access 287 | 288 | Parameters in the `Vagrantfile` can be retrieved via method call 289 | of array access. 290 | 291 | config.user['repository']['project'] # Array access 292 | config.user.repository.project # Method access 293 | 294 | You can even mix the two if you want, but we do not recommend 295 | it since its always better to be consistent: 296 | 297 | config.user['repository'].project # Mixed access 298 | config.user.repository['project'] # Mixed access 299 | 300 | Only the root key, i.e. `config.user`, cannot be access with 301 | both syntax, only the method syntax can be used since this 302 | is not provided by this plug-in but by Vagrant itself. 303 | 304 | ##### Tips 305 | 306 | Each non-final parameter (i.e a parameter that contains other parameters and 307 | not a scalar value) is in fact a [Bag](/lib/nugrant/bag.rb) 308 | object. You can call any defined methods on it. This object extends 309 | [Hash](http://ruby-doc.org/core-2.0/Hash.html) (itself including 310 | module [Enumerable](http://ruby-doc.org/core-2.0/Enumerable.html)). Hence, 311 | you can do neat things like iterating over your values or filtering them: 312 | 313 | config.user.application.users.each do |key, data| 314 | puts "Key #{key}: #{data}" 315 | end 316 | 317 | ##### Restricted keys 318 | 319 | Method access has the neat advantage of being beautiful to the 320 | eye. However, the drawback of using this retrieval syntax is 321 | that not all keys are permitted. As explained in the [Tips](#tips) 322 | section, the object you are calling when using method access 323 | is in fact of type [Bag](/lib/nugrant/bag.rb). Hence, if you 324 | are using a key which collapse with ones of Bag's keys, it will 325 | call the method instead of given you the value of you parameter. 326 | At best, you will get a weird behavior and at worst a stack 327 | trace when trying to access the parameter. 328 | 329 | If you really want to use one of the restricted keys, simply 330 | ensure to always use array access method. 331 | 332 | The list of restricted keys can be accessed using command 333 | `vagrant user restricted-keys`. 334 | 335 | #### Default values 336 | 337 | When using parameters, it is often needed so set default values 338 | for certain parameters so if the user does not define one, the 339 | default value will be picked up. 340 | 341 | For example, say you want a parameter that will hold the ssh 342 | port of the vm. This parameter will be accessible via the 343 | parameter `config.user.vm.ssh_port`. 344 | 345 | You can use the following snippet directly within your Vagrantfile 346 | to set a default value for this parameter: 347 | 348 | Vagrant.configure("2") do |config| 349 | config.user.defaults = { 350 | "vm" => { 351 | "ssh_port" => "3335" 352 | } 353 | } 354 | 355 | config.ssh.port config.user.vm.ssh_port 356 | end 357 | 358 | With this Vagrantfile, the parameter `config.user.vm.ssh_port` 359 | will default to `3335` in cases where it is not defined by the 360 | user. 361 | 362 | If the user decides to change it, he just has to set it in his 363 | own `.vagrantuser` and it will override the default value defined 364 | in the Vagrantfile. 365 | 366 | #### Array merge strategies 367 | 368 | As stated previously, when two arrays are merged together, 369 | the default strategy is to replace current array with new one. 370 | 371 | However, in some certain circumstances, you may need another 372 | behavior. Here the list of strategies that can be used. 373 | 374 | * `:replace` (*default*) 375 | With this strategy, the new array completely replace the 376 | current one. 377 | 378 | * `:concat` 379 | With this strategy, new array is appended to the end 380 | of current array when merged. The `Array` `+` operator 381 | is used to concatenante the two arrays. 382 | 383 | * `:extend` 384 | With this strategy, current array is extended with values 385 | from new one. The `Array` `|` (union) operator 386 | is used to extend the array. 387 | 388 | You can use the following snippet directly within your Vagrantfile 389 | to change the array merge strategy: 390 | 391 | Vagrant.configure("2") do |config| 392 | config.user.array_merge_strategy = :extend 393 | 394 | config.ssh.port config.user.vm.ssh_port 395 | end 396 | 397 | Note that you should change the array merge strategy before 398 | you access any keys because it's just once set that values 399 | are computed using the new strategy. 400 | 401 | If you specify an unsupported strategy, nothing will happen. 402 | 403 | #### Commands 404 | 405 | In this section, we describe the various vagrant commands defined 406 | by this plug-in that can be used to interact with it. 407 | 408 | ##### Parameters 409 | 410 | This command will print the currently defined parameters at the 411 | given location. All rules are respected when using this command. 412 | It is usefull to see what parameters are available and what are 413 | the current values of those parameters. 414 | 415 | Usage: 416 | 417 | > vagrant user parameters 418 | --- 419 | config: 420 | user: 421 | chef: 422 | cookbooks_path: /Users/Chef/kitchen/cookbooks 423 | nodes_path: /Users/Chef/kitchen/nodes 424 | roles_path: /Users/Chef/kitchen/roles 425 | 426 | Add flag `-h` (or `--help`) for description of the command and a 427 | list of available options. 428 | 429 | ##### Restricted Keys 430 | 431 | This command will print the keys that cannot be accessed using 432 | the method access syntax. 433 | 434 | Usage: 435 | 436 | > vagrant user restricted-keys 437 | 438 | ##### Env 439 | 440 | Sometimes, you would like to have acces to the different values 441 | stored in your `.vagrantuser` from environment variables: this is 442 | exactly what this section is meant to show you. 443 | 444 | By using one of the three methods below, you will be able to export 445 | (but also unset) environment variables from your current 446 | parameters as seen by Nugrant. 447 | 448 | You can see the commands that will be executed by simply 449 | calling the method: 450 | 451 | vagrant user env 452 | 453 | The name of the environment will be upper cased and full path of 454 | the key, without the `config.user` prefix, separated 455 | with `_`. For example, the key accessible using 456 | `config.user.db.user` and with value `root` would generate the 457 | export command: 458 | 459 | export DB_USER=root 460 | 461 | The value are escaped so it is possible to define value containing 462 | spaces for example. 463 | 464 | A last note about generate commands is that pre-existing environment 465 | variable are not taking in consideration by this command. So if 466 | an environment variable with name `DB_USER` already exist, it 467 | would be overwritten by an export command. 468 | 469 | Add flag `-h` (or `--help`) for description of the command and a 470 | list of available options. 471 | 472 | It's possible to export variables automatically on when Vagrant 473 | provision your VM. Refer to [Automatic Exporting section](#automatic-exporting) 474 | for more information. 475 | 476 | ###### Method #1 477 | 478 | If you plan to frequently use this feature, our best suggestion 479 | is to create a little bash script that will simply delegates 480 | to the real command. By having a bash script that calls the 481 | command, you will be able to easily export environment variables 482 | by sourcing the script. 483 | 484 | Create a file named `nugrant2env` somewhere accessible from 485 | the `$PATH` variable with the following content: 486 | 487 | #!/bin/env sh 488 | 489 | $(vagrant user env "$@") 490 | 491 | This script will simply delegates to the `vagrant user env` 492 | command and pass all arguments it receives to it. The 493 | magic happens because the command `vagrant user env` outputs 494 | the various export commands to the standard output. 495 | 496 | By sourcing the simple delegating bash script, the parameters 497 | seen by Nugrant will be available in your environment: 498 | 499 | . nugrant2env 500 | 501 | By default, export commands are generated. But you can pass 502 | some options to the `nugrant2env` script, For example, to 503 | generate the unset ones, add `--unset` (or simply `-u`). 504 | 505 | . nugrant2env --unset 506 | 507 | For a list of options, see the help of the command delegated 508 | to: 509 | 510 | vagrant user env -h 511 | 512 | ###### Method #2 513 | 514 | Use the command to generate a base script in the current 515 | directory that you will then source: 516 | 517 | vagrant user env --format script 518 | 519 | This will generate a script called `nugrant2env.sh` into the 520 | current directory. You then simply source this script: 521 | 522 | . nugrant2env.sh 523 | 524 | Using `vagrant user env -u --format script` will instead generate the bash 525 | script that will unset the environment variables. Don't forget 526 | to source it to unset variables. 527 | 528 | ###### Method #3 529 | 530 | Use the command to generate an [autoenv](https://github.com/kennethreitz/autoenv) 531 | file in the current directory. By using the [autoenv] project, anytime you 532 | will enter the project directory via the `cd` command, variables 533 | exported found in the `.env` file generated will be exported to 534 | your environment. 535 | 536 | vagrant user env --format autoenv 537 | 538 | This will generate a file called `.env` in the 539 | current directory. You then simply change to the directory 540 | where the `.env` file was generated to made exported variables 541 | available in your environment. 542 | 543 | cd .. 544 | cd 545 | 546 | Using `vagrant user env -u --format autoenv` will instead generate 547 | the autoenv file that will unset the environment variables. 548 | 549 | #### Automatic Exporting 550 | 551 | Running the right command each time variables change can be repetive 552 | for nothing. Why not let the computer do the hard work for us, they 553 | have been created for this after all. 554 | 555 | You can achieve this using the `config.user.auto_export` parameter. 556 | Defaulting to `false`, you can use it to export the variables 557 | to your desired format. When set to a valid value, each time 558 | vagrant will provision your VM, Nugrant will automatically 559 | export variables for your. 560 | 561 | Use the following configuration to export to [autoenv](https://github.com/kennethreitz/autoenv) 562 | script format. 563 | 564 | config.user.auto_export = :autoenv 565 | 566 | Use the following configuration to export to bash script. 567 | 568 | config.user.auto_export = :script 569 | 570 | You can also pass an array of formats if you would like to export to 571 | multiple formats at the same time. 572 | 573 | config.user.auto_export = [:autoenv, :script] 574 | 575 | 576 | The default filename for bash script (i.e. when using `:script` value) is 577 | `./nugrant2env.sh`. You can control this by using parameter 578 | `config.user.auto_export_script_path`: 579 | 580 | config.user.auto_export_script_path = "./script/env.sh" 581 | 582 | ### Library 583 | 584 | Using Nugrant as a library to handle parameters from various 585 | location is really easy. Two main classes need to be handled. 586 | 587 | First, you need to create a `Nugrant::Config` object. This 588 | configuration holds the values that needs to be customized 589 | by your own application. This includes the different parameters paths 590 | and the format of the parameters file. 591 | 592 | ## Contributing 593 | 594 | You can contribute by filling issues when something goes 595 | wrong or was not what you expected. I will do my best 596 | to fix the issue either in the code or in the documentation, 597 | where applicable. 598 | 599 | You can also send pull requests for any feature or improvement 600 | you think should be included in this plug-in. I will evaluate 601 | each of them and merge them as fast as possible. 602 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "rake/testtask" 3 | 4 | # Immediately sync all stdout so that tools like buildbot can 5 | # immediately load in the output. 6 | $stdout.sync = true 7 | $stderr.sync = true 8 | 9 | # Change to the directory of this file. 10 | Dir.chdir(File.expand_path("../", __FILE__)) 11 | 12 | Bundler::GemHelper.install_tasks 13 | 14 | Rake::TestTask.new do |task| 15 | task.test_files = FileList['test/**/test*.rb'] 16 | end 17 | 18 | desc "Run tests" 19 | task :default => :test 20 | task :tests => :test 21 | 22 | -------------------------------------------------------------------------------- /lib/nugrant.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'nugrant/config' 3 | require 'nugrant/parameters' 4 | 5 | # 1.8 Compatibility check 6 | unless defined?(KeyError) 7 | class KeyError < IndexError 8 | end 9 | end 10 | 11 | module Nugrant 12 | def self.setup_i18n() 13 | I18n.load_path << File.expand_path("locales/en.yml", Nugrant.source_root) 14 | I18n.reload! 15 | end 16 | 17 | def self.source_root 18 | @source_root ||= Pathname.new(File.expand_path("../../", __FILE__)) 19 | end 20 | end 21 | 22 | if defined?(Vagrant) 23 | Nugrant.setup_i18n() 24 | 25 | case 26 | when defined?(Vagrant::Plugin::V2) 27 | require 'nugrant/vagrant/v2/plugin' 28 | else 29 | raise RuntimeError, "Vagrant [#{Vagrant::VERSION}] is not supported by Nugrant." 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/nugrant/bag.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant/config' 2 | 3 | module Nugrant 4 | class Bag < Hash 5 | 6 | ## 7 | # Create a new Bag object which holds key/value pairs. 8 | # The Bag object inherits from the Hash object, the main 9 | # differences with a normal Hash are indifferent access 10 | # (symbol or string) and method access (via method call). 11 | # 12 | # Hash objects in the map are converted to Bag. This ensure 13 | # proper nesting of functionality. 14 | # 15 | # =| Arguments 16 | # * `elements` 17 | # The initial elements the bag should be built with it.' 18 | # Must be an object responding to `each` and accepting 19 | # a block with two arguments: `key, value`. Defaults to 20 | # the empty hash. 21 | # 22 | # * `config` 23 | # A Nugrant::Config object or hash passed to Nugrant::Config 24 | # constructor. Used for `key_error` handler. 25 | # 26 | def initialize(elements = {}, config = {}) 27 | super() 28 | 29 | @__config = Config::convert(config) 30 | 31 | (elements || {}).each do |key, value| 32 | self[key] = value 33 | end 34 | end 35 | 36 | def config=(config) 37 | @__config = Config::convert(config) 38 | end 39 | 40 | def method_missing(method, *args, &block) 41 | self[method] 42 | end 43 | 44 | ## 45 | ### Enumerable Overridden Methods (for string & symbol indifferent access) 46 | ## 47 | 48 | def count(item) 49 | super(__try_convert_item(item)) 50 | end 51 | 52 | def find_index(item = nil, &block) 53 | block_given? ? super(&block) : super(__try_convert_item(item)) 54 | end 55 | 56 | ## 57 | ### Hash Overridden Methods (for string & symbol indifferent access) 58 | ## 59 | 60 | def [](input) 61 | key = __convert_key(input) 62 | return @__config.key_error.call(key) if not key?(key) 63 | 64 | super(key) 65 | end 66 | 67 | def []=(input, value) 68 | super(__convert_key(input), __convert_value(value)) 69 | end 70 | 71 | def assoc(key) 72 | super(__convert_key(key)) 73 | end 74 | 75 | def delete(key) 76 | super(__convert_key(key)) 77 | end 78 | 79 | def fetch(key, default = nil) 80 | super(__convert_key(key), default) 81 | end 82 | 83 | def has_key?(key) 84 | super(__convert_key(key)) 85 | end 86 | 87 | def include?(item) 88 | super(__try_convert_item(item)) 89 | end 90 | 91 | def key?(key) 92 | super(__convert_key(key)) 93 | end 94 | 95 | def member?(item) 96 | super(__try_convert_item(item)) 97 | end 98 | 99 | def dup() 100 | self.class.new(self, @__config.dup()) 101 | end 102 | 103 | def merge(other, options = {}) 104 | result = dup() 105 | result.merge!(other) 106 | end 107 | 108 | def merge!(other, options = {}) 109 | other.each do |key, value| 110 | current = __get(key) 111 | case 112 | when current == nil 113 | self[key] = value 114 | 115 | when current.kind_of?(Hash) && value.kind_of?(Hash) 116 | current.merge!(value, options) 117 | 118 | when current.kind_of?(Array) && value.kind_of?(Array) 119 | strategy = options[:array_merge_strategy] 120 | if not Nugrant::Config.supported_array_merge_strategy(strategy) 121 | strategy = @__config.array_merge_strategy 122 | end 123 | 124 | self[key] = send("__#{strategy}_array_merge", current, value) 125 | 126 | when value != nil 127 | self[key] = value 128 | end 129 | end 130 | 131 | self 132 | end 133 | 134 | def store(key, value) 135 | self[key] = value 136 | end 137 | 138 | def to_hash(options = {}) 139 | return {} if empty?() 140 | 141 | use_string_key = options[:use_string_key] 142 | 143 | Hash[map do |key, value| 144 | key = use_string_key ? key.to_s() : key 145 | value = __convert_value_to_hash(value, options) 146 | 147 | [key, value] 148 | end] 149 | end 150 | 151 | def walk(path = [], &block) 152 | each do |key, value| 153 | nested_bag = value.kind_of?(Nugrant::Bag) 154 | 155 | value.walk(path + [key], &block) if nested_bag 156 | yield path + [key], key, value if not nested_bag 157 | end 158 | end 159 | 160 | alias_method :to_ary, :to_a 161 | 162 | ## 163 | ### Private Methods 164 | ## 165 | 166 | private 167 | 168 | def __convert_key(key) 169 | return key.to_s().to_sym() if !key.nil? && key.respond_to?(:to_s) 170 | 171 | raise ArgumentError, "Key cannot be nil" if key.nil? 172 | raise ArgumentError, "Key cannot be converted to symbol, current value [#{key}] (#{key.class.name})" 173 | end 174 | 175 | ## 176 | # This function change value convertible to Bag into actual Bag. 177 | # This trick enable deeply using all Bag functionalities and also 178 | # ensures at the same time a deeply preventive copy since a new 179 | # instance is created for this nested structure. 180 | # 181 | # In addition, it also transforms array elements of type Hash into 182 | # Bag instance also. This enable indifferent access inside arrays 183 | # also. 184 | # 185 | # @param value The value to convert to bag if necessary 186 | # @return The converted value 187 | # 188 | def __convert_value(value) 189 | case 190 | # Converts Hash to Bag instance 191 | when value.kind_of?(Hash) 192 | Bag.new(value, @__config) 193 | 194 | # Converts Array elements to Bag instances if needed 195 | when value.kind_of?(Array) 196 | value.map(&self.method(:__convert_value)) 197 | 198 | # Keeps as-is other elements 199 | else 200 | value 201 | end 202 | end 203 | 204 | ## 205 | # This function does the reversion of value conversion to Bag 206 | # instances. It transforms Bag instances to Hash (using to_hash) 207 | # and array Bag elements to Hash (using to_hash). 208 | # 209 | # The options parameters are pass to the to_hash function 210 | # when invoked. 211 | # 212 | # @param value The value to convert to hash 213 | # @param options The options passed to the to_hash function 214 | # @return The converted value 215 | # 216 | def __convert_value_to_hash(value, options = {}) 217 | case 218 | # Converts Bag instance to Hash 219 | when value.kind_of?(Bag) 220 | value.to_hash(options) 221 | 222 | # Converts Array elements to Hash instances if needed 223 | when value.kind_of?(Array) 224 | value.map { |value| __convert_value_to_hash(value, options) } 225 | 226 | # Keeps as-is other elements 227 | else 228 | value 229 | end 230 | end 231 | 232 | def __get(key) 233 | # Calls Hash method [__convert_key(key)], used internally to retrieve value without raising Undefined parameter 234 | self.class.superclass.instance_method(:[]).bind(self).call(__convert_key(key)) 235 | end 236 | 237 | ## 238 | # The concat order is the reversed compared to others 239 | # because we assume that new values have precedence 240 | # over current ones. Hence, the are prepended to 241 | # current values. This is also logical for parameters 242 | # because of the order in which bags are merged. 243 | # 244 | def __concat_array_merge(current_array, new_array) 245 | new_array + current_array 246 | end 247 | 248 | def __extend_array_merge(current_array, new_array) 249 | current_array | new_array 250 | end 251 | 252 | def __replace_array_merge(current_array, new_array) 253 | new_array 254 | end 255 | 256 | def __try_convert_item(args) 257 | return [__convert_key(args[0]), args[1]] if args.kind_of?(Array) 258 | 259 | __convert_key(args) 260 | rescue 261 | args 262 | end 263 | end 264 | end 265 | -------------------------------------------------------------------------------- /lib/nugrant/config.rb: -------------------------------------------------------------------------------- 1 | require 'rbconfig' 2 | 3 | module Nugrant 4 | class Config 5 | DEFAULT_ARRAY_MERGE_STRATEGY = :replace 6 | DEFAULT_PARAMS_FILENAME = ".nuparams" 7 | DEFAULT_PARAMS_FORMAT = :yaml 8 | DEFAULT_AUTO_EXPORT = false 9 | 10 | SUPPORTED_ARRAY_MERGE_STRATEGIES = [:concat, :extend, :replace] 11 | SUPPORTED_PARAMS_FORMATS = [:json, :yaml] 12 | 13 | attr_reader :params_filename, :params_format, 14 | :current_path, :user_path, :system_path, 15 | :array_merge_strategy, :auto_export, :auto_export_script_path, 16 | :key_error, :parse_error 17 | 18 | attr_writer :array_merge_strategy, :auto_export, :auto_export_script_path 19 | 20 | ## 21 | # Convenience method to easily accept either a hash that will 22 | # be converted to a Nugrant::Config object or directly a config 23 | # object. 24 | def self.convert(config = {}) 25 | return config.kind_of?(Nugrant::Config) ? config : Nugrant::Config.new(config) 26 | end 27 | 28 | ## 29 | # Return the fully expanded path of the user parameters 30 | # default location that is used in the constructor. 31 | # 32 | def self.default_user_path() 33 | File.expand_path("~") 34 | end 35 | 36 | ## 37 | # Return the fully expanded path of the system parameters 38 | # default location that is used in the constructor. 39 | # 40 | def self.default_system_path() 41 | if Config.on_windows? 42 | return File.expand_path(ENV['PROGRAMDATA'] || ENV['ALLUSERSPROFILE']) 43 | end 44 | 45 | "/etc" 46 | end 47 | 48 | ## 49 | # Method to fix-up a received path. The fix-up do the follows 50 | # the following rules: 51 | # 52 | # 1. If the path is callable, call it to get the value. 53 | # 2. If value is nil, return default value. 54 | # 3. If value is a directory, return path + params_filename to it. 55 | # 4. Otherwise, return value 56 | # 57 | # @param path The path parameter received. 58 | # @param default The default path to use, can be a directory. 59 | # @param params_filename The params filename to append if path is a directory 60 | # 61 | # @return The fix-up path following rules defined above. 62 | # 63 | def self.fixup_path(path, default, params_filename) 64 | path = path.call if path.respond_to?(:call) 65 | 66 | path = File.expand_path(path || default) 67 | path = "#{path}/#{params_filename}" if ::File.directory?(path) 68 | 69 | path 70 | end 71 | 72 | def self.supported_array_merge_strategy(strategy) 73 | SUPPORTED_ARRAY_MERGE_STRATEGIES.include?(strategy) 74 | end 75 | 76 | def self.supported_params_format(format) 77 | SUPPORTED_PARAMS_FORMATS.include?(format) 78 | end 79 | 80 | ## 81 | # Return true if we are currently on a Windows platform. 82 | # 83 | def self.on_windows?() 84 | (RbConfig::CONFIG['host_os'].downcase =~ /mswin|mingw|cygwin/) != nil 85 | end 86 | 87 | ## 88 | # Creates a new config object that is used to configure an instance 89 | # of Nugrant::Parameters. See the list of options and how they interact 90 | # with Nugrant::Parameters. 91 | # 92 | # =| Options 93 | # * +:params_filename+ - The filename used to fetch parameters from. This 94 | # will be appended to various default locations. 95 | # Location are system, project and current that 96 | # can be tweaked individually by using the options 97 | # below. 98 | # Defaults => ".nuparams" 99 | # * +:params_format+ - The format in which parameters are to be parsed. 100 | # Presently supporting :yaml and :json. 101 | # Defaults => :yaml 102 | # * +:current_path+ - The current path has the highest precedence over 103 | # any other location. It can be be used for many purposes 104 | # depending on your usage. 105 | # * A path from where to read project parameters 106 | # * A path from where to read overriding parameters for a cli tool 107 | # * A path from where to read user specific settings not to be committed in a VCS 108 | # Defaults => "./#{@params_filename}" 109 | # * +:user_path+ - The user path is the location where the user 110 | # parameters should resides. The parameters loaded from this 111 | # location have the second highest precedence. 112 | # Defaults => "~/#{@params_filename}" 113 | # * +:system_path+ - The system path is the location where system wide 114 | # parameters should resides. The parameters loaded from this 115 | # location have the third highest precedence. 116 | # Defaults => Default system path depending on OS + @params_filename 117 | # * +:array_merge_strategy+ - This option controls how array values are merged together when merging 118 | # two Bag instances. Possible values are: 119 | # * :replace => Replace current values by new ones 120 | # * :extend => Merge current values with new ones 121 | # * :concat => Append new values to current ones 122 | # Defaults => The strategy :replace. 123 | # * +:key_error+ - A callback method receiving one argument, the key as a symbol, and that 124 | # deal with the error. If the callable does not 125 | # raise an exception, the result of it's execution is returned. 126 | # Defaults => A callable that throws a KeyError exception. 127 | # * +:parse_error+ - A callback method receiving two arguments, the offending filename and 128 | # the error object, that deal with the error. If the callable does not 129 | # raise an exception, the result of it's execution is returned. 130 | # Defaults => A callable that returns the empty hash. 131 | # * +:auto_export+ - Automatically export configuration to the specified format :autoenv or :script 132 | # * +:auto_export_script_path+ - The path where to write the export script, defaults to "./nugrant2env.sh" 133 | # 134 | def initialize(options = {}) 135 | @params_filename = options[:params_filename] || DEFAULT_PARAMS_FILENAME 136 | @params_format = options[:params_format] || DEFAULT_PARAMS_FORMAT 137 | @auto_export = options[:auto_export] || DEFAULT_AUTO_EXPORT 138 | @auto_export_script_path = options[:auto_export_script_path] || false # use default 139 | 140 | @current_path = Config.fixup_path(options[:current_path], ".", @params_filename) 141 | @user_path = Config.fixup_path(options[:user_path], Config.default_user_path(), @params_filename) 142 | @system_path = Config.fixup_path(options[:system_path], Config.default_system_path(), @params_filename) 143 | 144 | @array_merge_strategy = options[:array_merge_strategy] || :replace 145 | 146 | @key_error = options[:key_error] || Proc.new do |key| 147 | raise KeyError, "Undefined parameter '#{key}'" 148 | end 149 | 150 | @parse_error = options[:parse_error] || Proc.new do |filename, error| 151 | {} 152 | end 153 | 154 | validate() 155 | end 156 | 157 | def ==(other) 158 | self.class.equal?(other.class) && 159 | instance_variables.all? do |variable| 160 | instance_variable_get(variable) == other.instance_variable_get(variable) 161 | end 162 | end 163 | 164 | def [](key) 165 | instance_variable_get("@#{key}") 166 | rescue 167 | nil 168 | end 169 | 170 | def merge(other) 171 | result = dup() 172 | result.merge!(other) 173 | end 174 | 175 | def merge!(other) 176 | other.instance_variables.each do |variable| 177 | instance_variable_set(variable, other.instance_variable_get(variable)) if instance_variables.include?(variable) 178 | end 179 | 180 | self 181 | end 182 | 183 | def to_h() 184 | Hash[instance_variables.map do |variable| 185 | [variable[1..-1].to_sym, instance_variable_get(variable)] 186 | end] 187 | end 188 | 189 | alias_method :to_hash, :to_h 190 | 191 | def validate() 192 | raise ArgumentError, 193 | "Invalid value for :params_format. \ 194 | The format [#{@params_format}] is currently not supported." if not Config.supported_params_format(@params_format) 195 | 196 | raise ArgumentError, 197 | "Invalid value for :array_merge_strategy. \ 198 | The array merge strategy [#{@array_merge_strategy}] is currently not supported." if not Config.supported_array_merge_strategy(@array_merge_strategy) 199 | end 200 | end 201 | end 202 | -------------------------------------------------------------------------------- /lib/nugrant/helper/bag.rb: -------------------------------------------------------------------------------- 1 | require 'multi_json' 2 | require 'yaml' 3 | 4 | require 'nugrant/bag' 5 | 6 | module Nugrant 7 | module Helper 8 | module Bag 9 | def self.read(filepath, filetype, config) 10 | Nugrant::Bag.new(parse_data(filepath, filetype, config), config) 11 | end 12 | 13 | def self.restricted_keys() 14 | Nugrant::Bag.instance_methods() 15 | end 16 | 17 | private 18 | 19 | def self.parse_data(filepath, filetype, config) 20 | return if not File.exist?(filepath) 21 | 22 | File.open(filepath, "rb") do |file| 23 | return send("parse_#{filetype}", file) 24 | end 25 | rescue => error 26 | config.parse_error.call(filepath, error) 27 | end 28 | 29 | def self.parse_json(io) 30 | MultiJson.load(io.read()) 31 | end 32 | 33 | def self.parse_yaml(io) 34 | YAML.load(io.read()) 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/nugrant/helper/env/exporter.rb: -------------------------------------------------------------------------------- 1 | require 'shellwords' 2 | 3 | require 'nugrant/bag' 4 | require 'nugrant/helper/env/namer' 5 | 6 | module Nugrant 7 | module Helper 8 | module Env 9 | module Exporter 10 | DEFAULT_AUTOENV_PATH = "./.env" 11 | DEFAULT_SCRIPT_PATH = "./nugrant2env.sh" 12 | 13 | VALID_EXPORTERS = [:autoenv, :script, :terminal] 14 | 15 | ## 16 | # Returns true if the exporter name received is a valid 17 | # valid export, false otherwise. 18 | # 19 | # @param exporter The exporter name to check validity 20 | # 21 | # @return true if exporter is valid, false otherwise. 22 | def self.valid?(exporter) 23 | VALID_EXPORTERS.include?(exporter) 24 | end 25 | 26 | ## 27 | # Creates an autoenv script containing the commands that are required 28 | # to export or unset a bunch of environment variables taken from the 29 | # bag. 30 | # 31 | # @param bag The bag to create the script for. 32 | # 33 | # @return (side-effect) Creates a script file containing commands 34 | # to export or unset environment variables for 35 | # bag. 36 | # 37 | # Options: 38 | # * :autoenv_path => The path where to write the script, defaults to `./.env`. 39 | # * :escape_value => If true, escape the value to export (or unset), default to true. 40 | # * :io => The io where the command should be written, default to nil which create the autoenv on disk. 41 | # * :namer => The namer used to transform bag segments into variable name, default to Namer::default(). 42 | # * :override => If true, variable a exported even when the override an existing env key, default to true. 43 | # * :type => The type of command, default to :export. 44 | # 45 | def self.autoenv_exporter(bag, options = {}) 46 | io = options[:io] || (File.open(File.expand_path(options[:autoenv_path] || DEFAULT_AUTOENV_PATH), "wb")) 47 | 48 | terminal_exporter(bag, options.merge({:io => io})) 49 | ensure 50 | io.close() if io 51 | end 52 | 53 | ## 54 | # Creates a bash script containing the commands that are required 55 | # to export or unset a bunch of environment variables taken from the 56 | # bag. 57 | # 58 | # @param bag The bag to create the script for. 59 | # 60 | # @return (side-effect) Creates a script file containing commands 61 | # to export or unset environment variables for 62 | # bag. 63 | # 64 | # Options: 65 | # * :escape_value => If true, escape the value to export (or unset), default to true. 66 | # * :io => The io where the command should be written, default to nil which create the script on disk. 67 | # * :namer => The namer used to transform bag segments into variable name, default to Namer::default(). 68 | # * :override => If true, variable a exported even when the override an existing env key, default to true. 69 | # * :script_path => The path where to write the script, defaults to `./nugrant2env.sh`. 70 | # * :type => The type of command, default to :export. 71 | # 72 | def self.script_exporter(bag, options = {}) 73 | io = options[:io] || (File.open(File.expand_path(options[:script_path] || DEFAULT_SCRIPT_PATH), "wb")) 74 | 75 | io.puts("#!/bin/env sh") 76 | io.puts() 77 | 78 | terminal_exporter(bag, options.merge({:io => io})) 79 | ensure 80 | io.close() if io 81 | end 82 | 83 | ## 84 | # Export to terminal the commands that are required 85 | # to export or unset a bunch of environment variables taken from the 86 | # bag. 87 | # 88 | # @param bag The bag to create the script for. 89 | # 90 | # @return (side-effect) Outputs to io the commands generated. 91 | # 92 | # Options: 93 | # * :escape_value => If true, escape the value to export (or unset), default to true. 94 | # * :io => The io where the command should be displayed, default to $stdout. 95 | # * :namer => The namer used to transform bag segments into variable name, default to Namer::default(). 96 | # * :override => If true, variable a exported even when the override an existing env key, default to true. 97 | # * :type => The type of command, default to :export. 98 | # 99 | def self.terminal_exporter(bag, options = {}) 100 | io = options[:io] || $stdout 101 | type = options[:type] || :export 102 | 103 | export(bag, options) do |key, value| 104 | io.puts(command(type, key, value, options)) 105 | end 106 | end 107 | 108 | ## 109 | # Generic function to export a bag. This walk the bag, 110 | # for each element, it creates the key using the namer 111 | # and then forward the key and value to the block if 112 | # the variable does not override an existing environment 113 | # variable or if options :override is set to true. 114 | # 115 | # @param bag The bag to export. 116 | # 117 | # @return (side-effect) Yields each key and value to a block 118 | # 119 | # Options: 120 | # * :namer => The namer used to transform bag parents into variable name, default to Namer::default(). 121 | # * :override => If true, variable a exported even when the override an existing env key, default to true. 122 | # 123 | def self.export(bag, options = {}) 124 | namer = options[:namer] || Env::Namer.default() 125 | override = options.fetch(:override, true) 126 | 127 | variables = {} 128 | bag.walk do |path, key, value| 129 | key = namer.call(path) 130 | 131 | variables[key] = value if override or not ENV[key] 132 | end 133 | 134 | variables.sort().each do |key, value| 135 | yield key, value 136 | end 137 | end 138 | 139 | ## 140 | # Given a key and a value, return a string representation 141 | # of the command type requested. Available types: 142 | # 143 | # * :export => A bash compatible export command 144 | # * :unset => A bash compatible export command 145 | # 146 | def self.command(type, key, value, options = {}) 147 | # TODO: Replace by a map type => function name 148 | case 149 | when type == :export 150 | export_command(key, value, options) 151 | when type == :unset 152 | unset_command(key, value, options) 153 | end 154 | end 155 | 156 | ## 157 | # Returns a string representation of the command 158 | # that needs to be used on the current platform 159 | # to export an environment variable. 160 | # 161 | # @param key The key of the environment variable to export. 162 | # It cannot be nil. 163 | # @param value The value of the environment variable to export 164 | # 165 | # @return The export command, as a string 166 | # 167 | # Options: 168 | # * :escape_value (true) => If true, escape the value to export. 169 | # 170 | def self.export_command(key, value, options = {}) 171 | value = value.to_s() 172 | value = Shellwords.escape(value) if options[:escape_value] == nil || options[:escape_value] 173 | 174 | # TODO: Handle platform differently 175 | "export #{key}=#{value}" 176 | end 177 | 178 | ## 179 | # Returns a string representation of the command 180 | # that needs to be used on the current platform 181 | # to unset an environment variable. 182 | # 183 | # @param key The key of the environment variable to export. 184 | # It cannot be nil. 185 | # 186 | # @return The unset command, as a string 187 | # 188 | def self.unset_command(key, value, options = {}) 189 | # TODO: Handle platform differently 190 | "unset #{key}" 191 | end 192 | end 193 | end 194 | end 195 | end 196 | -------------------------------------------------------------------------------- /lib/nugrant/helper/env/namer.rb: -------------------------------------------------------------------------------- 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 48 | -------------------------------------------------------------------------------- /lib/nugrant/helper/parameters.rb: -------------------------------------------------------------------------------- 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 13 | -------------------------------------------------------------------------------- /lib/nugrant/helper/stack.rb: -------------------------------------------------------------------------------- 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 87 | -------------------------------------------------------------------------------- /lib/nugrant/helper/yaml.rb: -------------------------------------------------------------------------------- 1 | module Nugrant 2 | module Helper 3 | class Yaml 4 | def self.format(string, options = {}) 5 | lines = string.send(string.respond_to?(:lines) ? :lines : :to_s).to_a 6 | lines = lines.drop(1) 7 | 8 | if options[:indent] 9 | indent_text = " " * options[:indent] 10 | lines = lines.map do |line| 11 | indent_text + line 12 | end 13 | end 14 | 15 | return lines.join("") 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/nugrant/mixin/parameters.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant/bag' 2 | require 'nugrant/config' 3 | require 'nugrant/helper/bag' 4 | 5 | module Nugrant 6 | module Mixin 7 | ## 8 | # Mixin module so it's possible to share parameters 9 | # logic between default Parameters class and Vagrant 10 | # implementation. 11 | # 12 | # This module delegates method missing to the final 13 | # bag hierarchy (@__all). This means that even if the class 14 | # including this module doesn't inherit Bag directly, 15 | # it act exactly like one. 16 | # 17 | # When including this module, you must respect an important 18 | # constraint. 19 | # 20 | # The including class must call `setup!` before starting using 21 | # parameters retrieval. This is usually performed in 22 | # the `initialize` method directly but could be in a different place 23 | # depending on the including class lifecycle. The call to `setup!` is 24 | # important to initialize all required instance variables. 25 | # 26 | # Here an example where `setup!` is called in constructor. Your constructor 27 | # does not need to have these arguments, they are there as an example. 28 | # 29 | # ``` 30 | # def initialize(defaults = {}, config = {}, options = {}) 31 | # setup!(defaults, config, options) 32 | # end 33 | # ``` 34 | # 35 | module Parameters 36 | attr_reader :__config, :__current, :__user, :__system, :__defaults, :__all 37 | 38 | def method_missing(method, *args, &block) 39 | case 40 | when @__all.class.method_defined?(method) 41 | @__all.send(method, *args, &block) 42 | else 43 | @__all[method] 44 | end 45 | end 46 | 47 | def array_merge_strategy 48 | @__config.array_merge_strategy 49 | end 50 | 51 | def auto_export 52 | @__config.auto_export 53 | end 54 | 55 | def auto_export_script_path 56 | @__config.auto_export_script_path 57 | end 58 | 59 | ## 60 | # Change the current array merge strategy for this parameters. 61 | # 62 | # @param strategy The new strategy to use. 63 | def array_merge_strategy=(strategy) 64 | return if not Nugrant::Config.supported_array_merge_strategy(strategy) 65 | 66 | @__config.array_merge_strategy = strategy 67 | 68 | # When array_merge_strategy change, we need to recompute parameters hierarchy 69 | compute_all!() 70 | end 71 | 72 | def auto_export=(auto_export) 73 | @__config.auto_export = auto_export 74 | end 75 | 76 | def auto_export_script_path=(path) 77 | @__config.auto_export_script_path = path 78 | end 79 | 80 | def defaults() 81 | @__defaults 82 | end 83 | 84 | ## 85 | # Set the new default values for the 86 | # various parameters contain by this instance. 87 | # This will call `compute_all!` to recompute 88 | # correct precedences. 89 | # 90 | # =| Attributes 91 | # * +elements+ - The new default elements 92 | # 93 | def defaults=(elements) 94 | @__defaults = Bag.new(elements, @__config) 95 | 96 | # When defaults change, we need to recompute parameters hierarchy 97 | compute_all!() 98 | end 99 | 100 | def merge(other) 101 | result = dup() 102 | result.merge!(other) 103 | end 104 | 105 | def merge!(other) 106 | @__config.merge!(other.__config) 107 | 108 | # Updated Bags' config 109 | @__current.config = @__config 110 | @__user.config = @__config 111 | @__system.config = @__config 112 | @__defaults.config = @__config 113 | 114 | # Merge other into Bags 115 | @__current.merge!(other.__current, :array_merge_strategy => :replace) 116 | @__user.merge!(other.__user, :array_merge_strategy => :replace) 117 | @__system.merge!(other.__system, :array_merge_strategy => :replace) 118 | @__defaults.merge!(other.__defaults, :array_merge_strategy => :replace) 119 | 120 | # Recompute all from merged Bags 121 | compute_all!() 122 | 123 | self 124 | end 125 | 126 | ## 127 | # Setup instance variables of the mixin. It will compute all parameters bags 128 | # (current, user, system, default and all) and stored them to these respective 129 | # instance variables: 130 | # 131 | # * @__current 132 | # * @__user 133 | # * @__system 134 | # * @__defaults 135 | # 136 | # =| Arguments 137 | # * `defaults` 138 | # A hash that is used as the initial data for the defaults bag. Defaults 139 | # to an empty hash. 140 | # 141 | # * `config` 142 | # A Nugrant::Config object or hash passed to Nugrant::Config 143 | # convert method. Used to determine where to find the various 144 | # bag data sources and other configuration options. 145 | # 146 | # Passed to nested structures that requires a Nugrant::Config object 147 | # like the Bag object and Helper::Bag module. 148 | # 149 | # * `options` 150 | # Options hash used by this method exclusively. No options yet, added 151 | # for future improvements. 152 | # 153 | def setup!(defaults = {}, config = {}, options = {}) 154 | @__config = Nugrant::Config::convert(config); 155 | 156 | @__defaults = Bag.new(defaults, @__config) 157 | @__current = Helper::Bag.read(@__config.current_path, @__config.params_format, @__config) 158 | @__user = Helper::Bag.read(@__config.user_path, @__config.params_format, @__config) 159 | @__system = Helper::Bag.read(@__config.system_path, @__config.params_format, @__config) 160 | 161 | compute_all!() 162 | end 163 | 164 | ## 165 | # Recompute the correct precedences by merging the various 166 | # bag in the right order and return the result as a Nugrant::Bag 167 | # object. 168 | # 169 | def compute_all!() 170 | @__all = Bag.new({}, @__config) 171 | @__all.merge!(@__defaults) 172 | @__all.merge!(@__system) 173 | @__all.merge!(@__user) 174 | @__all.merge!(@__current) 175 | end 176 | end 177 | end 178 | end 179 | -------------------------------------------------------------------------------- /lib/nugrant/parameters.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant/mixin/parameters' 2 | 3 | module Nugrant 4 | class Parameters 5 | 6 | include Mixin::Parameters 7 | 8 | ## 9 | # Create a new parameters object which holds completed 10 | # merged values. The following precedence is used to decide 11 | # which location has precedence over which location: 12 | # 13 | # (Highest) ------------------ (Lowest) 14 | # project < user < system < defaults 15 | # 16 | # =| Arguments 17 | # * `defaults` 18 | # Passed to Mixin::Parameters setup! method. See mixin 19 | # module for further information. 20 | # 21 | # * `config` 22 | # Passed to Mixin::Parameters setup! method. See mixin 23 | # module for further information. 24 | # 25 | def initialize(defaults = {}, config = {}, options = {}) 26 | setup!(defaults, config, options) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/errors.rb: -------------------------------------------------------------------------------- 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 | 11 | def compute_context() 12 | Helper::Stack.fetch_error_region(caller(), { 13 | :matcher => /(.+Vagrantfile):([0-9]+)/ 14 | }) 15 | end 16 | end 17 | 18 | class ParameterNotFoundError < NugrantVagrantError 19 | error_key(:parameter_not_found) 20 | 21 | def initialize(options = nil, *args) 22 | super({:context => compute_context()}.merge(options || {}), *args) 23 | end 24 | end 25 | 26 | class VagrantUserParseError < NugrantVagrantError 27 | error_key(:parse_error) 28 | 29 | def initialize(options = nil, *args) 30 | super(options, *args) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/action.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant/vagrant/v2/action/auto_export' 2 | 3 | module Nugrant 4 | module Vagrant 5 | module V2 6 | module Action 7 | class << self 8 | def auto_export 9 | @auto_export ||= ::Vagrant::Action::Builder.new.tap do |builder| 10 | builder.use Nugrant::Vagrant::V2::Action::AutoExport 11 | end 12 | end 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/action/auto_export.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant' 2 | require 'nugrant/helper/env/exporter' 3 | require 'nugrant/parameters' 4 | 5 | module Nugrant 6 | module Vagrant 7 | module V2 8 | module Action 9 | EnvExporter = Nugrant::Helper::Env::Exporter 10 | 11 | class AutoExport 12 | def initialize(app, env) 13 | @app = app 14 | @config = env[:machine].env.vagrantfile.config 15 | end 16 | 17 | def call(env) 18 | return @app.call(env) if not @config.user.auto_export 19 | 20 | options = { 21 | :type => :export, 22 | :script_path => @config.user.auto_export_script_path 23 | } 24 | 25 | Array(@config.user.auto_export).each do |exporter| 26 | if exporter == :terminal or not EnvExporter.valid?(exporter) 27 | env[:ui].warn("ERROR: Invalid config.user.auto_export value '#{exporter}'", :prefix => false) 28 | next 29 | end 30 | 31 | env[:ui].info("Configuration exported '#{exporter}'", :prefix => false) 32 | 33 | case 34 | when exporter == :script 35 | EnvExporter.script_exporter(@config.user.__all, options) 36 | when exporter == :autoenv 37 | EnvExporter.autoenv_exporter(@config.user.__all, options) 38 | end 39 | end 40 | end 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/command/env.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant' 2 | require 'nugrant/helper/env/exporter' 3 | require 'nugrant/parameters' 4 | 5 | module Nugrant 6 | module Vagrant 7 | module V2 8 | module Command 9 | class Env < ::Vagrant.plugin("2", :command) 10 | EnvExporter = Nugrant::Helper::Env::Exporter 11 | 12 | def self.synopsis 13 | "env utilities to export config.user as environment variables in host machine" 14 | end 15 | 16 | def initialize(arguments, environment) 17 | super(arguments, environment) 18 | 19 | @unset = false 20 | @format = :terminal 21 | @script_path = false 22 | @show_help = false 23 | end 24 | 25 | def create_parser() 26 | return OptionParser.new do |parser| 27 | parser.banner = "Usage: vagrant user env []" 28 | parser.separator "" 29 | 30 | parser.separator "Outputs the commands that should be executed to export\n" + 31 | "the various parameter as environment variables. By default,\n" + 32 | "existing ones are overridden. The --format argument can be used\n" + 33 | "to choose in which format the variables should be displayed.\n" + 34 | "Changing the format will also change where they are displayed.\n" 35 | parser.separator "" 36 | 37 | parser.separator "Available options:" 38 | parser.separator "" 39 | 40 | parser.on("-u", "--unset", "Generates commands needed to unset environment variables, default false") do 41 | @unset = true 42 | end 43 | 44 | parser.on("-f", "--format FORMAT", "Determines in what format variables are output, default to terminal") do |format| 45 | @format = format.to_sym() 46 | end 47 | 48 | parser.on("-s", "--script-path PATH", "Specifies path of the generated bash script, default to #{EnvExporter::DEFAULT_SCRIPT_PATH}") do |path| 49 | @script_path = path 50 | end 51 | 52 | parser.on("-h", "--help", "Prints this help") do 53 | @show_help = true 54 | end 55 | 56 | parser.separator "" 57 | parser.separator "Available formats:" 58 | parser.separator " autoenv Write commands to a file named `.env` in the current directory." 59 | parser.separator " See https://github.com/kennethreitz/autoenv for more info." 60 | parser.separator " terminal Write commands to terminal so they can be sourced." 61 | parser.separator " script Write commands to a bash script named `nugrant2env.sh` so it can be sourced." 62 | parser.separator "" 63 | end 64 | end 65 | 66 | def error(message, parser) 67 | @env.ui.info("ERROR: #{message}", :prefix => false) 68 | @env.ui.info("", :prefix => false) 69 | 70 | help(parser) 71 | 72 | return 1 73 | end 74 | 75 | def help(parser) 76 | @env.ui.info(parser.help, :prefix => false) 77 | end 78 | 79 | def execute 80 | parser = create_parser() 81 | arguments = parse_options(parser) 82 | 83 | return error("Invalid format value '#{@format}'", parser) if not EnvExporter.valid?(@format) 84 | return help(parser) if @show_help 85 | 86 | @logger.debug("Nugrant 'Env'") 87 | with_target_vms(arguments) do |vm| 88 | parameters = vm.config.user 89 | if not parameters 90 | @env.ui.info("# Vm '#{vm.name}' : unable to retrieve `config.user`, report as bug https://github.com/maoueh/nugrant/issues", :prefix => false) 91 | next 92 | end 93 | 94 | bag = parameters.__all 95 | 96 | options = { 97 | :type => @unset ? :unset : :export, 98 | :script_path => @script_path 99 | } 100 | 101 | case 102 | when @format == :script 103 | EnvExporter.script_exporter(bag, options) 104 | when @format == :autoenv 105 | EnvExporter.autoenv_exporter(bag, options) 106 | when @format == :terminal 107 | EnvExporter.terminal_exporter(bag, options) 108 | end 109 | 110 | # No need to execute for the other VMs 111 | return 0 112 | end 113 | end 114 | end 115 | end 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/command/parameters.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant' 2 | require 'nugrant/helper/yaml' 3 | require 'nugrant/vagrant/v2/helper' 4 | 5 | module Nugrant 6 | module Vagrant 7 | module V2 8 | module Command 9 | class Parameters < ::Vagrant.plugin("2", :command) 10 | def self.synopsis 11 | "prints parameters hierarchy as seen by Nugrant" 12 | end 13 | 14 | def initialize(arguments, environment) 15 | super(arguments, environment) 16 | 17 | @show_help = false 18 | @show_defaults = false 19 | @show_system = false 20 | @show_user = false 21 | @show_project = false 22 | end 23 | 24 | def create_parser() 25 | return OptionParser.new do |parser| 26 | parser.banner = "Usage: vagrant user parameters []" 27 | parser.separator "" 28 | 29 | parser.separator "Available options:" 30 | parser.separator "" 31 | 32 | parser.on("-h", "--help", "Prints this help") do 33 | @show_help = true 34 | end 35 | 36 | parser.on("-d", "--defaults", "Show only defaults parameters") do 37 | @show_defaults = true 38 | end 39 | 40 | parser.on("-s", "--system", "Show only system parameters") do 41 | @show_system = true 42 | end 43 | 44 | parser.on("-u", "--user", "Show only user parameters") do 45 | @show_user = true 46 | end 47 | 48 | parser.on("-p", "--project", "Show only project parameters") do 49 | @show_project = true 50 | end 51 | 52 | parser.separator "" 53 | parser.separator "When no options is provided, the command prints the names and values \n" + 54 | "of all parameters that would be available for usage in the Vagrantfile.\n" + 55 | "The hierarchy of the parameters is respected, so the final values are\n" + 56 | "displayed." 57 | end 58 | end 59 | 60 | def execute 61 | parser = create_parser() 62 | arguments = parse_options(parser) 63 | 64 | return help(parser) if @show_help 65 | 66 | @logger.debug("'Parameters' each target VM...") 67 | with_target_vms(arguments) do |vm| 68 | parameters = vm.config.user 69 | if not parameters 70 | @env.ui.info("# Vm '#{vm.name}' : unable to retrieve `config.user`, report as bug https://github.com/maoueh/nugrant/issues", :prefix => false) 71 | next 72 | end 73 | 74 | @env.ui.info("# Vm '#{vm.name}'", :prefix => false) 75 | 76 | defaults(parameters) if @show_defaults 77 | system(parameters) if @show_system 78 | user(parameters) if @show_user 79 | project(parameters) if @show_project 80 | 81 | all(parameters) if !@show_defaults && !@show_system && !@show_user && !@show_project 82 | end 83 | 84 | return 0 85 | end 86 | 87 | def help(parser) 88 | @env.ui.info(parser.help, :prefix => false) 89 | end 90 | 91 | def defaults(parameters) 92 | print_bag("Defaults", parameters.__defaults) 93 | end 94 | 95 | def system(parameters) 96 | print_bag("System", parameters.__system) 97 | end 98 | 99 | def user(parameters) 100 | print_bag("User", parameters.__user) 101 | end 102 | 103 | def project(parameters) 104 | print_bag("Project", parameters.__current) 105 | end 106 | 107 | def all(parameters) 108 | print_bag("All", parameters.__all) 109 | end 110 | 111 | def print_bag(kind, bag) 112 | if !bag || bag.empty?() 113 | print_header(kind) 114 | @env.ui.info(" Empty", :prefix => false) 115 | @env.ui.info("", :prefix => false) 116 | return 117 | end 118 | 119 | print_parameters(kind, { 120 | 'config' => { 121 | 'user' => bag.to_hash(:use_string_key => true) 122 | } 123 | }) 124 | end 125 | 126 | def print_parameters(kind, hash) 127 | string = Nugrant::Helper::Yaml.format(hash.to_yaml, :indent => 1) 128 | used_restricted_keys = Helper::get_used_restricted_keys(hash, Helper::get_restricted_keys()) 129 | 130 | print_warning(used_restricted_keys) if !used_restricted_keys.empty?() 131 | 132 | print_header(kind) 133 | @env.ui.info(string, :prefix => false) 134 | @env.ui.info("", :prefix => false) 135 | end 136 | 137 | def print_header(kind, length = 50) 138 | @env.ui.info(" #{kind.capitalize} Parameters", :prefix => false) 139 | @env.ui.info(" " + "-" * length, :prefix => false) 140 | end 141 | 142 | def print_warning(used_restricted_keys) 143 | @env.ui.info("", :prefix => false) 144 | @env.ui.info(" You are using some restricted keys. Method access (using .) will", :prefix => false) 145 | @env.ui.info(" not work, only array access ([]) will. Offending keys:", :prefix => false) 146 | @env.ui.info(" #{used_restricted_keys.join(", ")}", :prefix => false) 147 | @env.ui.info("", :prefix => false) 148 | end 149 | end 150 | end 151 | end 152 | end 153 | end 154 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/command/restricted_keys.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant' 2 | require 'nugrant/vagrant/v2/helper' 3 | 4 | module Nugrant 5 | module Vagrant 6 | module V2 7 | module Command 8 | class RestrictedKeys < ::Vagrant.plugin("2", :command) 9 | def self.synopsis 10 | "prints list of restricted keys for method access" 11 | end 12 | 13 | def initialize(arguments, environment) 14 | super(arguments, environment) 15 | 16 | @show_help = false 17 | end 18 | 19 | def create_parser() 20 | return OptionParser.new do |parser| 21 | parser.banner = "Usage: vagrant user restricted-keys" 22 | parser.separator "" 23 | 24 | parser.separator "Available options:" 25 | parser.separator "" 26 | 27 | parser.on("-h", "--help", "Prints this help") do 28 | @show_help = true 29 | end 30 | 31 | parser.separator "" 32 | parser.separator "Prints keys that cannot be accessed using the method access syntax\n" + 33 | "(`config.user.`). Use array access syntax (`config.user['']`)\n" + 34 | "if you really want to use of the restricted keys.\n" 35 | end 36 | end 37 | 38 | def execute 39 | parser = create_parser() 40 | arguments = parse_options(parser) 41 | 42 | return help(parser) if @show_help 43 | 44 | @env.ui.info("The following keys are restricted, i.e. that method access (`config.user.`)", :prefix => false) 45 | @env.ui.info("will not work. If you really want to use a restricted key, use array access ", :prefix => false) 46 | @env.ui.info("instead (`config.user['']`).", :prefix => false) 47 | @env.ui.info("", :prefix => false) 48 | 49 | @env.ui.info("You can run `vagrant user parameters` to check if your config currently defines", :prefix => false) 50 | @env.ui.info("one or more restricted keys shown below.", :prefix => false) 51 | @env.ui.info("", :prefix => false) 52 | 53 | restricted_keys = Helper::get_restricted_keys() 54 | @env.ui.info(restricted_keys.sort().join(", "), :prefix => false) 55 | end 56 | 57 | def help(parser) 58 | @env.ui.info(parser.help, :prefix => false) 59 | end 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/command/root.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant' 2 | require 'nugrant/vagrant/v2/command/env' 3 | require 'nugrant/vagrant/v2/command/parameters' 4 | require 'nugrant/vagrant/v2/command/restricted_keys' 5 | require 'nugrant/version' 6 | 7 | module Nugrant 8 | module Vagrant 9 | module V2 10 | module Command 11 | class Root < ::Vagrant.plugin("2", :command) 12 | def self.synopsis 13 | "manage Nugrant user defined parameters (config.user)" 14 | end 15 | 16 | def initialize(arguments, environment) 17 | super(arguments, environment) 18 | 19 | @arguments, @subcommand, @subarguments = split_main_and_subcommand(arguments) 20 | 21 | # Change super class available arguments to main ones only 22 | @argv = @arguments 23 | 24 | @subcommands = ::Vagrant::Registry.new() 25 | @subcommands.register(:env) do 26 | Command::Env 27 | end 28 | 29 | @subcommands.register(:parameters) do 30 | Command::Parameters 31 | end 32 | 33 | @subcommands.register(:'restricted-keys') do 34 | Command::RestrictedKeys 35 | end 36 | 37 | @show_help = false 38 | @show_version = false 39 | end 40 | 41 | def create_parser() 42 | return OptionParser.new do |parser| 43 | parser.banner = "Usage: vagrant user [-h] [-v] []" 44 | 45 | parser.separator "" 46 | parser.on("-h", "--help", "Prints this help") do 47 | @show_help = true 48 | end 49 | 50 | parser.on("-v", "--version", "Prints plugin version and exit.") do 51 | @show_version = true 52 | end 53 | 54 | parser.separator "" 55 | parser.separator "Available subcommands:" 56 | 57 | keys = [] 58 | @subcommands.each { |key, value| keys << key.to_s } 59 | 60 | keys.sort.each do |key| 61 | parser.separator " #{key}" 62 | end 63 | 64 | parser.separator "" 65 | parser.separator "For help on any individual command run `vagrant user -h`" 66 | end 67 | end 68 | 69 | def execute 70 | parser = create_parser() 71 | arguments = parse_options(parser) 72 | 73 | return version() if @show_version 74 | return help(parser) if @show_help 75 | 76 | command_class = @subcommands.get(@subcommand.to_sym) if @subcommand 77 | return help(parser) if !command_class || !@subcommand 78 | 79 | @logger.debug("Invoking nugrant command class: #{command_class} #{@subarguments.inspect}") 80 | 81 | command_class.new(@subarguments, @env).execute 82 | end 83 | 84 | def help(parser) 85 | @env.ui.info(parser.help, :prefix => false) 86 | end 87 | 88 | def version() 89 | @env.ui.info("Nugrant version #{Nugrant::VERSION}", :prefix => false) 90 | end 91 | end 92 | end 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/config/user.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant/mixin/parameters' 2 | require 'nugrant/vagrant/errors' 3 | require 'nugrant/vagrant/v2/helper' 4 | 5 | module Nugrant 6 | module Vagrant 7 | module V2 8 | module Config 9 | class User < ::Vagrant.plugin("2", :config) 10 | 11 | include Mixin::Parameters 12 | 13 | def initialize(defaults = {}, config = {}) 14 | setup!(defaults, 15 | :params_filename => ".vagrantuser", 16 | :current_path => Helper.find_project_path(), 17 | :key_error => Proc.new do |key| 18 | raise Errors::ParameterNotFoundError, :key => key.to_s 19 | end, 20 | :parse_error => Proc.new do |filename, error| 21 | raise Errors::VagrantUserParseError, :filename => filename.to_s, :error => error 22 | end 23 | ) 24 | end 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/helper.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | 3 | require 'nugrant' 4 | require 'nugrant/bag' 5 | require 'nugrant/vagrant/v2/config/user' 6 | 7 | module Nugrant 8 | module Vagrant 9 | module V2 10 | class Helper 11 | 12 | ## 13 | # The project path is the path where the top-most (loaded last) 14 | # Vagrantfile resides. It can be considered the project root for 15 | # this environment. 16 | # 17 | # Copied from `lib\vagrant\environment.rb#532` (tag: v1.6.2) 18 | # 19 | # @return [String] The project path to use. 20 | # 21 | def self.find_project_path() 22 | vagrantfile_name = ENV["VAGRANT_VAGRANTFILE"] 23 | vagrantfile_name = [vagrantfile_name] if vagrantfile_name && !vagrantfile_name.is_a?(Array) 24 | 25 | root_finder = lambda do |path| 26 | vagrantfile = find_vagrantfile(path, vagrantfile_name) 27 | 28 | return path if vagrantfile 29 | return nil if path.root? || !File.exist?(path) 30 | 31 | root_finder.call(path.parent) 32 | end 33 | 34 | root_finder.call(get_vagrant_working_directory()) 35 | end 36 | 37 | ## 38 | # Finds the Vagrantfile in the given directory. 39 | # 40 | # Copied from `lib\vagrant\environment.rb#732` (tag: v1.6.2) 41 | # 42 | # @param [Pathname] path Path to search in. 43 | # @return [Pathname] 44 | # 45 | def self.find_vagrantfile(search_path, filenames = nil) 46 | filenames ||= ["Vagrantfile", "vagrantfile"] 47 | filenames.each do |vagrantfile| 48 | current_path = search_path.join(vagrantfile) 49 | return current_path if current_path.file? 50 | end 51 | 52 | nil 53 | end 54 | 55 | ## 56 | # Returns Vagrant working directory to use. 57 | # 58 | # Copied from `lib\vagrant\environment.rb#80` (tag: v1.6.2) 59 | # 60 | # @return [Pathname] The working directory to start search in. 61 | # 62 | def self.get_vagrant_working_directory() 63 | cwd = nil 64 | 65 | # Set the default working directory to look for the vagrantfile 66 | cwd ||= ENV["VAGRANT_CWD"] if ENV.has_key?("VAGRANT_CWD") 67 | cwd ||= Dir.pwd 68 | cwd = Pathname.new(cwd) 69 | 70 | if !cwd.directory? 71 | raise Errors::EnvironmentNonExistentCWD, cwd: cwd.to_s 72 | end 73 | 74 | cwd = cwd.expand_path 75 | end 76 | 77 | def self.get_restricted_keys() 78 | bag_methods = Nugrant::Bag.instance_methods 79 | parameters_methods = V2::Config::User.instance_methods 80 | 81 | (bag_methods | parameters_methods).map(&:to_s) 82 | end 83 | 84 | def self.get_used_restricted_keys(hash, restricted_keys) 85 | keys = [] 86 | hash.each do |key, value| 87 | keys << key if restricted_keys.include?(key) 88 | keys += get_used_restricted_keys(value, restricted_keys) if value.kind_of?(Hash) 89 | end 90 | 91 | keys.uniq 92 | end 93 | end 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /lib/nugrant/vagrant/v2/plugin.rb: -------------------------------------------------------------------------------- 1 | require 'nugrant/vagrant/v2/action' 2 | 3 | module Nugrant 4 | module Vagrant 5 | module V2 6 | class Plugin < ::Vagrant.plugin("2") 7 | name "Nugrant" 8 | description <<-DESC 9 | Plugin to define and use user specific parameters from various location inside your Vagrantfile. 10 | DESC 11 | 12 | class << self 13 | def provision(hook) 14 | hook.before(::Vagrant::Action::Builtin::Provision, Nugrant::Vagrant::V2::Action.auto_export) 15 | end 16 | end 17 | 18 | action_hook(:nugrant_provision, :machine_action_up, &method(:provision)) 19 | action_hook(:nugrant_provision, :machine_action_reload, &method(:provision)) 20 | action_hook(:nugrant_provision, :machine_action_provision, &method(:provision)) 21 | 22 | command "user" do 23 | require_relative "command/root" 24 | Command::Root 25 | end 26 | 27 | config "user" do 28 | require_relative "config/user" 29 | Config::User 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/nugrant/version.rb: -------------------------------------------------------------------------------- 1 | module Nugrant 2 | VERSION = "2.1.5.dev1" 3 | end 4 | -------------------------------------------------------------------------------- /locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | nugrant: 3 | vagrant: 4 | errors: 5 | parameter_not_found: |- 6 | Nugrant: Parameter '%{key}' was not found, is it defined in 7 | your .vagrantuser file? Here where we think the error 8 | could be in your Vagrantfile: 9 | 10 | %{context} 11 | 12 | If you think the parameter '%{key}' should have been found, don't 13 | hesitate to fill an issue @ https://github.com/maoueh/nugrant/issues. 14 | 15 | parse_error: |- 16 | Nugrant: Vagrant user file could not be parsed correctly, 17 | the file is probably in an invalid state. 18 | 19 | File: %{filename} 20 | Error: %{error} 21 | 22 | If you think this is an error, don't hesitate to fill an 23 | issue @ https://github.com/maoueh/nugrant/issues. 24 | 25 | -------------------------------------------------------------------------------- /locales/fr.yml: -------------------------------------------------------------------------------- 1 | en: 2 | nugrant: 3 | vagrant: 4 | errors: 5 | parameter_not_found: |- 6 | Nugrant: Le paramètre '%{key}' n'a pu être trouvé, est-il définie 7 | dans votre fichier .vagrantuser ? Voici où nous pensons que l'erreur 8 | se trouve dans votre fichier Vagrantfile: 9 | 10 | %{context} 11 | 12 | Si vous croyez que le paramètre '%{key}' devrait être trouvé, n'hésitez 13 | pas à rapporter le problème @ https://github.com/maoueh/nugrant/issues. 14 | 15 | parse_error: |- 16 | Nugrant: Le fichier de paramètres Vagrant n'a pu être lu correctement, 17 | le fichier contient probablement une erreur de syntaxe. 18 | 19 | File: %{filename} 20 | Error: %{error} 21 | 22 | Si vous croyez que ceci est un bug, n'hésitez pas à rapporter le 23 | problème @ https://github.com/maoueh/nugrant/issues. 24 | 25 | -------------------------------------------------------------------------------- /nugrant.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | 5 | require 'nugrant/version' 6 | 7 | Gem::Specification.new do |gem| 8 | gem.name = "nugrant" 9 | gem.version = Nugrant::VERSION 10 | gem.authors = ["Matthieu Vachon"] 11 | gem.email = ["matthieu.o.vachon@gmail.com"] 12 | gem.homepage = "https://github.com/maoueh/nugrant" 13 | gem.summary = "Library to handle user specific parameters from various location." 14 | gem.description = <<-EOF 15 | Nugrant is a library to easily handle parameters that need to be 16 | injected into an application via different sources (system, user, 17 | project, defaults). 18 | 19 | Nugrant can also be directly used as a Vagrant plugin. By activating 20 | this gem with Vagrant, it will be possible to define user specific 21 | parameters that will be injected directly into the Vagrantfile. This 22 | is useful if you need to share a Vagrantfile to multiple developers 23 | but would like to customize some parameters for each user differently. 24 | EOF 25 | 26 | gem.files = `git ls-files`.split($/) 27 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 28 | gem.test_files = gem.files.grep(%r{^(test/lib)/}) 29 | gem.require_paths = ["lib"] 30 | 31 | gem.add_dependency "multi_json", "~> 1.8" 32 | end 33 | -------------------------------------------------------------------------------- /test/lib/nugrant/helper/env/test_exporter.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'nugrant/bag' 4 | require 'nugrant/helper/env/exporter' 5 | 6 | module Nugrant 7 | module Helper 8 | module Env 9 | class TestExporter < ::Minitest::Test 10 | def create_bag(parameters) 11 | return Nugrant::Bag.new(parameters) 12 | end 13 | 14 | def assert_export(expected, key, value, options = {}) 15 | actual = Env::Exporter.command(:export, key, value, options) 16 | 17 | assert_equal(expected, actual) 18 | end 19 | 20 | def assert_unset(expected, key, options = {}) 21 | actual = Env::Exporter.command(:unset, key, options) 22 | 23 | assert_equal(expected, actual) 24 | end 25 | 26 | def assert_autoenv_exporter(expected, bag, options = {}) 27 | io = StringIO.new() 28 | Env::Exporter.autoenv_exporter(bag, options.merge({:io => io})) 29 | 30 | actual = io.string().split(/\r?\n/) 31 | 32 | assert_equal(expected, actual) 33 | end 34 | 35 | def assert_script_exporter(expected, bag, options = {}) 36 | io = StringIO.new() 37 | Env::Exporter.script_exporter(bag, options.merge({:io => io})) 38 | 39 | actual = io.string().split(/\r?\n/) 40 | 41 | assert_equal(expected, actual) 42 | end 43 | 44 | def assert_terminal_exporter(expected, bag, options = {}) 45 | io = StringIO.new() 46 | Env::Exporter.terminal_exporter(bag, options.merge({:io => io})) 47 | 48 | actual = io.string().split(/\r?\n/) 49 | 50 | assert_equal(expected, actual) 51 | end 52 | 53 | def assert_unset_commands(expected, bag, options = {}) 54 | actual = Env::Exporter.unset_commands(bag, options) 55 | 56 | assert_equal(expected, actual) 57 | end 58 | 59 | def test_valid_exporter 60 | assert_equal(true, Env::Exporter.valid?(:autoenv)) 61 | assert_equal(true, Env::Exporter.valid?(:script)) 62 | assert_equal(true, Env::Exporter.valid?(:terminal)) 63 | assert_equal(false, Env::Exporter.valid?(:foreman)) 64 | end 65 | 66 | def test_export_command 67 | assert_export("export TEST=\\\"running\\ with\\ space\\\"", "TEST", "\"running with space\"") 68 | assert_export("export TEST=running with space", "TEST", "running with space", :escape_value => false) 69 | end 70 | 71 | def test_unset_command 72 | assert_unset("unset TEST", "TEST") 73 | end 74 | 75 | def test_terminal_exporter_export 76 | bag = create_bag({ 77 | :level1 => { 78 | :level2 => { 79 | :first => "first with space", 80 | :second => "\"second\\" 81 | }, 82 | :third => "third" 83 | }, 84 | :existing => "downcase", 85 | }) 86 | 87 | stub_env(:existing => "exist", :EXISTING => "exist") do 88 | assert_terminal_exporter([ 89 | "export EXISTING=downcase", 90 | "export LEVEL1_LEVEL2_FIRST=first\\ with\\ space", 91 | "export LEVEL1_LEVEL2_SECOND=\\\"second\\\\", 92 | "export LEVEL1_THIRD=third", 93 | ], bag) 94 | 95 | assert_terminal_exporter([ 96 | "export LEVEL1_LEVEL2_FIRST=first\\ with\\ space", 97 | "export LEVEL1_LEVEL2_SECOND=\\\"second\\\\", 98 | "export LEVEL1_THIRD=third", 99 | ], bag, :override => false) 100 | 101 | assert_terminal_exporter([ 102 | "export EXISTING=downcase", 103 | "export LEVEL1_LEVEL2_FIRST=first with space", 104 | "export LEVEL1_LEVEL2_SECOND=\"second\\", 105 | "export LEVEL1_THIRD=third", 106 | ], bag, :type => :export, :override => true, :escape_value => false) 107 | 108 | default_namer = Env::Namer.default(".") 109 | prefix_namer = Env::Namer.prefix("CONFIG", default_namer) 110 | 111 | assert_terminal_exporter([ 112 | "export CONFIG.EXISTING=downcase", 113 | "export CONFIG.LEVEL1.LEVEL2.FIRST=first with space", 114 | "export CONFIG.LEVEL1.LEVEL2.SECOND=\"second\\", 115 | "export CONFIG.LEVEL1.THIRD=third", 116 | ], bag, :override => true, :escape_value => false, :namer => prefix_namer) 117 | end 118 | end 119 | 120 | def test_terminal_exporter_unset 121 | bag = create_bag({ 122 | :level1 => { 123 | :level2 => { 124 | :first => "first", 125 | :second => "second" 126 | }, 127 | :third => "third" 128 | }, 129 | :existing => "downcase", 130 | }) 131 | 132 | stub_env(:existing => "exist", :EXISTING => "exist") do 133 | assert_terminal_exporter([ 134 | "unset EXISTING", 135 | "unset LEVEL1_LEVEL2_FIRST", 136 | "unset LEVEL1_LEVEL2_SECOND", 137 | "unset LEVEL1_THIRD", 138 | ], bag, :type => :unset) 139 | 140 | assert_terminal_exporter([ 141 | "unset LEVEL1_LEVEL2_FIRST", 142 | "unset LEVEL1_LEVEL2_SECOND", 143 | "unset LEVEL1_THIRD", 144 | ], bag, :override => false, :type => :unset) 145 | 146 | default_namer = Env::Namer.default(".") 147 | prefix_namer = Env::Namer.prefix("CONFIG", default_namer) 148 | 149 | assert_terminal_exporter([ 150 | "unset CONFIG.EXISTING", 151 | "unset CONFIG.LEVEL1.LEVEL2.FIRST", 152 | "unset CONFIG.LEVEL1.LEVEL2.SECOND", 153 | "unset CONFIG.LEVEL1.THIRD", 154 | ], bag, :override => true, :namer => prefix_namer, :type => :unset) 155 | end 156 | end 157 | 158 | def test_autoenv_exporter 159 | bag = create_bag({ 160 | :level1 => { 161 | :level2 => { 162 | :first => "first", 163 | :second => "second" 164 | }, 165 | :third => "third" 166 | }, 167 | :existing => "downcase", 168 | }) 169 | 170 | assert_autoenv_exporter([ 171 | "export EXISTING=downcase", 172 | "export LEVEL1_LEVEL2_FIRST=first", 173 | "export LEVEL1_LEVEL2_SECOND=second", 174 | "export LEVEL1_THIRD=third", 175 | ], bag, :type => :export) 176 | 177 | assert_autoenv_exporter([ 178 | "unset EXISTING", 179 | "unset LEVEL1_LEVEL2_FIRST", 180 | "unset LEVEL1_LEVEL2_SECOND", 181 | "unset LEVEL1_THIRD", 182 | ], bag, :type => :unset) 183 | end 184 | 185 | def test_script_exporter 186 | bag = create_bag({ 187 | :level1 => { 188 | :level2 => { 189 | :first => "first", 190 | :second => "second" 191 | }, 192 | :third => "third" 193 | }, 194 | :existing => "downcase", 195 | }) 196 | 197 | assert_script_exporter([ 198 | "#!/bin/env sh", 199 | "", 200 | "export EXISTING=downcase", 201 | "export LEVEL1_LEVEL2_FIRST=first", 202 | "export LEVEL1_LEVEL2_SECOND=second", 203 | "export LEVEL1_THIRD=third", 204 | ], bag, :type => :export) 205 | 206 | assert_script_exporter([ 207 | "#!/bin/env sh", 208 | "", 209 | "unset EXISTING", 210 | "unset LEVEL1_LEVEL2_FIRST", 211 | "unset LEVEL1_LEVEL2_SECOND", 212 | "unset LEVEL1_THIRD", 213 | ], bag, :type => :unset) 214 | end 215 | 216 | def replace_env(variables) 217 | ENV.clear() 218 | 219 | variables = Hash[variables.map do |name, value| 220 | [name.to_s, value] 221 | end] 222 | 223 | ENV.update(variables) 224 | end 225 | 226 | def stub_env(new = {}) 227 | old = ENV.to_hash() 228 | 229 | replace_env(new) 230 | yield 231 | 232 | ensure 233 | replace_env(old) 234 | end 235 | end 236 | end 237 | end 238 | end 239 | -------------------------------------------------------------------------------- /test/lib/nugrant/helper/test_bag.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'nugrant/helper/bag' 4 | 5 | module Nugrant 6 | module Helper 7 | class TestBag < ::Minitest::Test 8 | def test_restricted_keys_contains_hash_ones 9 | keys = Helper::Bag.restricted_keys() 10 | Hash.instance_methods.each do |method| 11 | assert_includes(keys, method, "Restricted keys must include Hash method #{method}") 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/lib/nugrant/helper/test_parameters.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'nugrant/bag' 4 | require 'nugrant/helper/parameters' 5 | 6 | module Nugrant 7 | module Helper 8 | class TestParameters < ::Minitest::Test 9 | def test_restricted_keys_contains_hash_ones 10 | keys = Helper::Parameters.restricted_keys() 11 | Nugrant::Bag.instance_methods.each do |method| 12 | assert_includes(keys, method, "Restricted keys must include Nugrant::Bag method #{method}") 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/nugrant/helper/test_stack.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'nugrant/helper/stack' 4 | 5 | module Nugrant 6 | module Helper 7 | class TestStack < ::Minitest::Test 8 | def create_stack(options = {}) 9 | pattern = options[:pattern] || "Vagrantfile:%s" 10 | count = options[:count] || 4 11 | 12 | stack = [] 13 | (0..count).each do |index| 14 | stack << pattern.gsub("%s", index.to_s()) 15 | end 16 | 17 | stack 18 | end 19 | 20 | def create_location(name, line) 21 | resource_path = File.expand_path("#{File.dirname(__FILE__)}/../../../resources/vagrantfiles") 22 | 23 | {:file => "#{resource_path}/#{name}", :line => line} 24 | end 25 | 26 | def assert_error_location(expected, entry, matcher = nil) 27 | assert_equal(expected, Stack::extract_error_location(entry, :matcher => matcher), "Not exact error location") 28 | end 29 | 30 | def assert_error_region(expected, region) 31 | expected_lines = expected.split("\n") 32 | region_lines = region.split("\n") 33 | 34 | expected_count = expected_lines.length() 35 | actual_count = region_lines.length() 36 | 37 | assert_equal(expected_count, actual_count, "Region different line count") 38 | 39 | expected_lines.each_with_index do |expected_line, index| 40 | assert_equal(expected_line.strip(), region_lines[index].strip(), "Line ##{index} are not equals") 41 | end 42 | end 43 | 44 | def test_fetch_error_region_from_location() 45 | location = create_location("v2.defaults_using_symbol", 4) 46 | error_region = Stack::fetch_error_region_from_location(location) 47 | expected_region = <<-EOT 48 | 1: Vagrant.configure("2") do |config| 49 | 2: config.user.defaults = { 50 | 3: :single => 1, 51 | 4:>> :local => { 52 | 5: :first => "value1", 53 | 6: :second => "value2" 54 | 7: } 55 | 8: } 56 | EOT 57 | 58 | assert_error_region(expected_region, error_region) 59 | end 60 | 61 | def test_fetch_error_region_from_location_custom_prefix() 62 | location = create_location("v2.defaults_using_symbol", 4) 63 | error_region = Stack::fetch_error_region_from_location(location, :prefix => "**") 64 | expected_region = <<-EOT 65 | **1: Vagrant.configure(\"2\") do |config| 66 | **2: config.user.defaults = { 67 | **3: :single => 1, 68 | **4:>> :local => { 69 | **5: :first => "value1", 70 | **6: :second => "value2" 71 | **7: } 72 | **8: } 73 | EOT 74 | 75 | assert_error_region(expected_region, error_region) 76 | end 77 | 78 | def test_fetch_error_region_from_location_custom_width() 79 | location = create_location("v2.defaults_using_symbol", 4) 80 | error_region = Stack::fetch_error_region_from_location(location, :width => 2) 81 | expected_region = <<-EOT 82 | 2: config.user.defaults = { 83 | 3: :single => 1, 84 | 4:>> :local => { 85 | 5: :first => "value1", 86 | 6: :second => "value2" 87 | EOT 88 | 89 | assert_error_region(expected_region, error_region) 90 | end 91 | 92 | def test_fetch_error_region_from_location_wrong_location() 93 | location = {:file => nil, :line => nil} 94 | assert_equal("Unknown", Stack::fetch_error_region_from_location(location)) 95 | assert_equal("Failed", Stack::fetch_error_region_from_location(location, :unknown => "Failed")) 96 | 97 | location = {:file => "Vagrantfile", :line => nil} 98 | assert_equal("Vagrantfile", Stack::fetch_error_region_from_location(location)) 99 | 100 | location = {:file => "NonExistingVagrantfile", :line => 4} 101 | assert_equal("NonExistingVagrantfile:4", Stack::fetch_error_region_from_location(location)) 102 | end 103 | 104 | def test_find_entry() 105 | entries = ["First", "Second:", "Third:a", "Fourth:4"] 106 | 107 | assert_equal("Fourth:4", Stack::find_entry(entries)) 108 | assert_equal("Third:a", Stack::find_entry(entries, :matcher => /^(.+):([a-z]+)/)) 109 | end 110 | 111 | def test_extract_error_location_default_matcher() 112 | # Matches 113 | assert_error_location({:file => "/work/irb/workspace.rb", :line => 80}, "/work/irb/workspace.rb:80:in `eval'") 114 | assert_error_location({:file => "workspace.rb", :line => 80}, "workspace.rb:80:in `eval'") 115 | assert_error_location({:file => "/work/irb/workspace.rb", :line => 80}, "/work/irb/workspace.rb:80") 116 | 117 | # No match 118 | assert_error_location({:file => nil, :line => nil}, "/work/irb/workspace.rb?80") 119 | assert_error_location({:file => nil, :line => nil}, "/work/irb/workspace.rb") 120 | assert_error_location({:file =>nil, :line => nil}, "") 121 | end 122 | 123 | def test_extract_error_location_custom_matcher() 124 | # Matches 125 | assert_error_location( 126 | {:file => "/work/Vagrantfile", :line => 80}, 127 | "/work/Vagrantfile:80:in `eval'", 128 | /(.*Vagrantfile):([0-9]+)/ 129 | ) 130 | 131 | assert_error_location( 132 | {:file => "Vagrantfile", :line => 80}, 133 | "Vagrantfile:80:in `eval'", 134 | /(.*Vagrantfile):([0-9]+)/ 135 | ) 136 | 137 | assert_error_location( 138 | {:file => "/work/irb/Vagrantfile", :line => 80}, 139 | "/work/irb/Vagrantfile:80", 140 | /(.*Vagrantfile):([0-9]+)/ 141 | ) 142 | 143 | # Partial match 144 | assert_error_location( 145 | {:file => "/work/Vagrantfile", :line => nil}, 146 | "/work/Vagrantfile:80:in `eval'", 147 | /(.*Vagrantfile)/ 148 | ) 149 | end 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /test/lib/nugrant/test_bag.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'nugrant/bag' 4 | require 'nugrant/helper/bag' 5 | 6 | module Nugrant 7 | class TestBag < ::Minitest::Test 8 | def run_test_bag(parameters) 9 | bag = create_bag(parameters) 10 | 11 | assert_bag_equal(parameters, bag) 12 | end 13 | 14 | def test_bag() 15 | run_test_bag({:first => "value1", :second => "value2"}) 16 | 17 | run_test_bag({ 18 | :first => { 19 | :level1 => "value1", 20 | :level2 => "value2", 21 | }, 22 | :second => { 23 | :level1 => "value3", 24 | :level2 => "value4", 25 | }, 26 | :third => "value5" 27 | }) 28 | 29 | run_test_bag({ 30 | :first => { 31 | :level1 => { 32 | :level11 => "value1", 33 | :level12 => "value2", 34 | }, 35 | :level2 => { 36 | :level21 => "value3", 37 | :level22 => "value4", 38 | }, 39 | :level3 => "value5", 40 | }, 41 | :second => { 42 | :level1 => { 43 | :level11 => "value6", 44 | :level12 => "value7", 45 | }, 46 | :level2 => { 47 | :level21 => "value8", 48 | :level22 => "value9", 49 | }, 50 | :level3 => "value10", 51 | }, 52 | :third => "value11" 53 | }) 54 | end 55 | 56 | def test_undefined_value() 57 | bag = create_bag({:value => "one"}) 58 | 59 | assert_raises(KeyError) do 60 | bag.invalid_value 61 | end 62 | 63 | assert_raises(KeyError) do 64 | bag["invalid_value"] 65 | end 66 | 67 | assert_raises(KeyError) do 68 | bag[:invalid_value] 69 | end 70 | end 71 | 72 | def test_to_hash() 73 | hash = create_bag({}).to_hash() 74 | 75 | assert_is_hash_exclusive(hash) 76 | assert_equal({}, hash) 77 | 78 | hash = create_bag({"value" => {:one => "value", "two" => "value"}}).to_hash() 79 | 80 | assert_is_hash_exclusive(hash) 81 | assert_is_hash_exclusive(hash[:value]) 82 | assert_kind_of(String, hash[:value][:one]) 83 | assert_kind_of(String, hash[:value][:two]) 84 | assert_equal({:value => {:one => "value", :two => "value"}}, hash) 85 | end 86 | 87 | def test_to_hash_array_with_bag() 88 | hash = create_bag({:a => [{:b => "1", :c => [{"d" => "2"}]}]}).to_hash() 89 | 90 | assert_is_hash_exclusive(hash) 91 | assert_kind_of(Array, hash[:a]) 92 | assert_is_hash_exclusive(hash[:a][0]) 93 | assert_kind_of(String, hash[:a][0][:b]) 94 | assert_kind_of(Array, hash[:a][0][:c]) 95 | assert_is_hash_exclusive(hash[:a][0][:c][0]) 96 | assert_kind_of(String, hash[:a][0][:c][0][:d]) 97 | 98 | assert_equal({:a => [{:b => "1", :c => [{:d => "2"}]}]}, hash) 99 | end 100 | 101 | def test_to_hash_array_with_bag_string_key() 102 | hash = create_bag({:a => [{:b => "1", :c => [{"d" => "2"}]}]}).to_hash(use_string_key: true) 103 | 104 | assert_is_hash_exclusive(hash) 105 | assert_kind_of(Array, hash["a"]) 106 | assert_is_hash_exclusive(hash["a"][0]) 107 | assert_kind_of(String, hash["a"][0]["b"]) 108 | assert_kind_of(Array, hash["a"][0]["c"]) 109 | assert_is_hash_exclusive(hash["a"][0]["c"][0]) 110 | assert_kind_of(String, hash["a"][0]["c"][0]["d"]) 111 | 112 | assert_equal({"a" => [{"b" => "1", "c" => [{"d" => "2"}]}]}, hash) 113 | end 114 | 115 | def test_merge_array_replace() 116 | # Replace should be the default case 117 | bag1 = create_bag({"first" => [1, 2]}) 118 | bag2 = create_bag({:first => [2, 3]}) 119 | 120 | bag1.merge!(bag2); 121 | 122 | assert_equal({:first => [2, 3]}, bag1.to_hash()) 123 | 124 | bag1 = create_bag({"first" => [1, 2]}) 125 | bag2 = create_bag({:first => "string"}) 126 | 127 | bag1.merge!(bag2); 128 | 129 | assert_equal({:first => "string"}, bag1.to_hash()) 130 | end 131 | 132 | def test_merge_array_extend() 133 | bag1 = create_bag({"first" => [1, 2]}, :array_merge_strategy => :extend) 134 | bag2 = create_bag({:first => [2, 3]}) 135 | 136 | bag1.merge!(bag2); 137 | 138 | assert_equal({:first => [1, 2, 3]}, bag1.to_hash()) 139 | 140 | bag1 = create_bag({"first" => [1, 2]}, :array_merge_strategy => :extend) 141 | bag2 = create_bag({:first => "string"}) 142 | 143 | bag1.merge!(bag2); 144 | 145 | assert_equal({:first => "string"}, bag1.to_hash()) 146 | end 147 | 148 | def test_merge_array_concat() 149 | bag1 = create_bag({"first" => [1, 2]}, :array_merge_strategy => :concat) 150 | bag2 = create_bag({:first => [2, 3]}) 151 | 152 | bag1.merge!(bag2); 153 | 154 | assert_equal({:first => [2, 3, 1, 2]}, bag1.to_hash()) 155 | 156 | bag1 = create_bag({"first" => [1, 2]}, :array_merge_strategy => :concat) 157 | bag2 = create_bag({:first => "string"}) 158 | 159 | bag1.merge!(bag2); 160 | 161 | assert_equal({:first => "string"}, bag1.to_hash()) 162 | end 163 | 164 | def test_merge_hash_keeps_indifferent_access 165 | bag1 = create_bag({"first" => {:second => [1, 2]}}) 166 | bag1.merge!({:third => "three", "first" => {:second => [3, 4]}}) 167 | 168 | assert_equal({:first => {:second => [3, 4]}, :third => "three"}, bag1.to_hash()) 169 | 170 | assert_indifferent_access_equal({:second => [3, 4]}, bag1, :first) 171 | assert_indifferent_access_equal([3, 4], bag1["first"], :second) 172 | end 173 | 174 | def test_set_a_slot_with_a_hash_keeps_indifferent_access 175 | bag1 = create_bag({}) 176 | bag1["first"] = {:second => [1, 2]} 177 | 178 | assert_indifferent_access_equal({:second => [1, 2]}, bag1, :first) 179 | assert_indifferent_access_equal([1, 2], bag1["first"], :second) 180 | end 181 | 182 | def test_nil_key() 183 | assert_raises(ArgumentError) do 184 | create_bag({nil => "value"}) 185 | end 186 | 187 | parameters = create_bag({}) 188 | 189 | assert_raises(ArgumentError) do 190 | parameters[nil] = 1 191 | end 192 | 193 | assert_raises(ArgumentError) do 194 | parameters[nil] 195 | end 196 | 197 | assert_raises(ArgumentError) do 198 | parameters.method_missing(nil) 199 | end 200 | end 201 | 202 | def test_restricted_keys_are_still_accessible 203 | keys = Helper::Bag.restricted_keys() 204 | bag = create_bag(Hash[ 205 | keys.map do |key| 206 | [key, "#{key.to_s} - value"] 207 | end 208 | ]) 209 | 210 | keys.each do |key| 211 | assert_equal("#{key.to_s} - value", bag[key.to_s], "bag[#{key.to_s}]") 212 | assert_equal("#{key.to_s} - value", bag[key.to_sym], "bag[#{key.to_sym}]") 213 | end 214 | end 215 | 216 | def test_numeric_keys_converted_to_string 217 | bag1 = create_bag({1 => "value1"}) 218 | 219 | assert_indifferent_access_equal("value1", bag1, :'1') 220 | end 221 | 222 | def test_custom_key_error_handler 223 | bag = create_bag({:value => "one"}, :key_error => Proc.new do |key| 224 | raise IndexError 225 | end) 226 | 227 | assert_raises(IndexError) do 228 | bag.invalid_value 229 | end 230 | 231 | assert_raises(IndexError) do 232 | bag["invalid_value"] 233 | end 234 | 235 | assert_raises(IndexError) do 236 | bag[:invalid_value] 237 | end 238 | end 239 | 240 | def test_custom_key_error_handler_returns_value 241 | bag = create_bag({:value => "one"}, :key_error => Proc.new do |key| 242 | "Some value" 243 | end) 244 | 245 | assert_indifferent_access_equal("Some value", bag, :invalid_value) 246 | end 247 | 248 | def test_walk_bag 249 | bag = create_bag({ 250 | :first => { 251 | :level1 => "value1", 252 | :level2 => "value2", 253 | }, 254 | :second => { 255 | :level1 => "value3", 256 | :level2 => "value4", 257 | }, 258 | :third => "value5" 259 | }) 260 | 261 | lines = [] 262 | bag.walk do |path, key, value| 263 | lines << "Path (#{path}), Key (#{key}), Value (#{value})" 264 | end 265 | 266 | assert_equal([ 267 | "Path ([:first, :level1]), Key (level1), Value (value1)", 268 | "Path ([:first, :level2]), Key (level2), Value (value2)", 269 | "Path ([:second, :level1]), Key (level1), Value (value3)", 270 | "Path ([:second, :level2]), Key (level2), Value (value4)", 271 | "Path ([:third]), Key (third), Value (value5)", 272 | ], lines) 273 | end 274 | 275 | def test_merge 276 | bag1 = create_bag({ 277 | :first => { 278 | :level1 => "value1", 279 | :level2 => "value2", 280 | }, 281 | :second => { 282 | :level1 => "value3", 283 | :level2 => "value4", 284 | }, 285 | :third => "value5" 286 | }) 287 | 288 | bag2 = create_bag({ 289 | :first => { 290 | :level2 => "overriden2", 291 | }, 292 | :second => { 293 | :level1 => "value3", 294 | :level2 => "value4", 295 | }, 296 | :third => "overriden5" 297 | }) 298 | 299 | bag3 = bag1.merge(bag2) 300 | 301 | refute_same(bag1, bag3) 302 | refute_same(bag2, bag3) 303 | 304 | assert_equal(Nugrant::Bag, bag3.class) 305 | 306 | assert_indifferent_access_equal("value1", bag3['first'], :level1) 307 | assert_indifferent_access_equal("overriden2", bag3['first'], :level2) 308 | assert_indifferent_access_equal("value3", bag3['second'], :level1) 309 | assert_indifferent_access_equal("value4", bag3['second'], :level2) 310 | assert_indifferent_access_equal("overriden5", bag3, :third) 311 | end 312 | 313 | def test_merge! 314 | bag1 = create_bag({ 315 | :first => { 316 | :level1 => "value1", 317 | :level2 => "value2", 318 | }, 319 | :second => { 320 | :level1 => "value3", 321 | :level2 => "value4", 322 | }, 323 | :third => "value5" 324 | }) 325 | 326 | bag2 = create_bag({ 327 | :first => { 328 | :level2 => "overriden2", 329 | }, 330 | :second => { 331 | :level1 => "value3", 332 | :level2 => "value4", 333 | }, 334 | :third => "overriden5" 335 | }) 336 | 337 | bag3 = bag1.merge!(bag2) 338 | 339 | assert_same(bag1, bag3) 340 | refute_same(bag2, bag3) 341 | 342 | assert_equal(Nugrant::Bag, bag3.class) 343 | 344 | assert_indifferent_access_equal("value1", bag3['first'], :level1) 345 | assert_indifferent_access_equal("overriden2", bag3['first'], :level2) 346 | assert_indifferent_access_equal("value3", bag3['second'], :level1) 347 | assert_indifferent_access_equal("value4", bag3['second'], :level2) 348 | assert_indifferent_access_equal("overriden5", bag3, :third) 349 | end 350 | 351 | def test_merge_hash() 352 | bag1 = create_bag({"first" => {:second => [1, 2]}}, :array_merge_strategy => :extend) 353 | 354 | bag1.merge!({"first" => {:second => [2, 3]}}); 355 | 356 | assert_equal({:first => {:second => [1, 2, 3]}}, bag1.to_hash()) 357 | end 358 | 359 | def test_merge_custom_array_merge_strategy() 360 | bag1 = create_bag({"first" => {:second => [1, 2]}}, :array_merge_strategy => :extend) 361 | 362 | bag1.merge!({"first" => {:second => [2, 3]}}, :array_merge_strategy => :replace); 363 | 364 | assert_equal({:first => {:second => [2, 3]}}, bag1.to_hash()) 365 | end 366 | 367 | def test_merge_custom_invalid_array_merge_strategy() 368 | bag1 = create_bag({"first" => {:second => [1, 2]}}, :array_merge_strategy => :extend) 369 | bag2 = bag1.merge({"first" => {:second => [2, 3]}}, :array_merge_strategy => nil); 370 | 371 | assert_equal({:first => {:second => [1, 2, 3]}}, bag2.to_hash()) 372 | 373 | bag2 = bag1.merge({"first" => {:second => [2, 3]}}, :array_merge_strategy => :invalid); 374 | 375 | assert_equal({:first => {:second => [1, 2, 3]}}, bag2.to_hash()) 376 | end 377 | 378 | def test_change_config 379 | bag1 = create_bag({"first" => [1, 2]}, :array_merge_strategy => :extend) 380 | bag2 = create_bag({:first => [2, 3]}) 381 | 382 | bag3 = bag1.merge!(bag2); 383 | bag3.config = { 384 | :array_merge_strategy => :concat 385 | } 386 | 387 | bag3.merge!({"first" => [1, 2, 3]}) 388 | 389 | assert_equal([1, 2, 3, 1, 2, 3], bag3['first']) 390 | end 391 | 392 | def test_indifferent_access_in_array 393 | bag = create_bag({:a => [{:b => 1, :c => [{"d" => 1}]}]}) 394 | 395 | assert_indifferent_access_equal(1, bag['a'][0]['c'][0], :d) 396 | end 397 | 398 | ## Helpers & Assertions 399 | 400 | def create_bag(elements, options = {}) 401 | return Bag.new(elements, options) 402 | end 403 | 404 | def assert_is_hash_exclusive(input) 405 | assert_equal(true, input.kind_of?(Hash), "#{input} should be a kind of Hash") 406 | assert_equal(false, input.kind_of?(Bag), "#{input} should not be a kind of Bag") 407 | end 408 | 409 | def assert_indifferent_access_equal(expected, bag, key) 410 | assert_symbol_access_equal(expected, bag, key) 411 | assert_string_access_equal(expected, bag, key) 412 | end 413 | 414 | def assert_string_access_equal(expected, bag, key) 415 | assert_equal(expected, bag[key.to_s], "bag[#{key.to_s}]") 416 | end 417 | 418 | def assert_symbol_access_equal(expected, bag, key) 419 | assert_equal(expected, bag.method_missing(key.to_sym), "bag.#{key.to_sym}") 420 | assert_equal(expected, bag[key.to_sym], "bag[#{key.to_sym}]") 421 | end 422 | 423 | def assert_indifferent_access_bag_equal(expected, bag, key) 424 | assert_string_access_bag_equal(expected, bag, key) 425 | assert_symbol_access_bag_equal(expected, bag, key) 426 | end 427 | 428 | def assert_string_access_bag_equal(expected, bag, key) 429 | assert_bag_equal(expected, bag[key.to_s]) 430 | end 431 | 432 | def assert_symbol_access_bag_equal(expected, bag, key) 433 | assert_bag_equal(expected, bag.method_missing(key.to_sym)) 434 | assert_bag_equal(expected, bag[key.to_sym]) 435 | end 436 | 437 | def assert_bag_equal(expected, bag) 438 | assert_kind_of(Bag, bag) 439 | 440 | expected.each do |key, expected_value| 441 | if not expected_value.kind_of?(Hash) 442 | assert_indifferent_access_equal(expected_value, bag, key) 443 | next 444 | end 445 | 446 | assert_indifferent_access_bag_equal(expected_value, bag, key) 447 | end 448 | end 449 | end 450 | end 451 | -------------------------------------------------------------------------------- /test/lib/nugrant/test_config.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'tmpdir' 3 | 4 | require 'nugrant/config' 5 | 6 | module Nugrant 7 | class TestConfig < ::Minitest::Test 8 | def setup 9 | @default_param_filename = Nugrant::Config::DEFAULT_PARAMS_FILENAME 10 | 11 | @old_working_dir = Dir.getwd() 12 | @user_dir = Nugrant::Config.default_user_path() 13 | @system_dir = Nugrant::Config.default_system_path() 14 | 15 | Dir.chdir(Dir.tmpdir()) 16 | 17 | @current_dir = Dir.getwd() 18 | end 19 | 20 | def teardown 21 | Dir.chdir(@old_working_dir) 22 | 23 | @old_working_dir = nil 24 | @current_dir = nil 25 | @user_dir = nil 26 | @system_dir = nil 27 | end 28 | 29 | def test_default_values 30 | config = Nugrant::Config.new() 31 | 32 | assert_equal(@default_param_filename, config.params_filename()) 33 | assert_equal("#{@current_dir}/#{@default_param_filename}", config.current_path()) 34 | assert_equal("#{@user_dir}/#{@default_param_filename}", config.user_path()) 35 | assert_equal("#{@system_dir}/#{@default_param_filename}", config.system_path()) 36 | end 37 | 38 | def test_custom_params_filename 39 | config = Nugrant::Config.new({:params_filename => ".customparams"}) 40 | 41 | assert_equal(".customparams", config.params_filename()) 42 | assert_equal("#{@current_dir}/.customparams", config.current_path()) 43 | assert_equal("#{@user_dir}/.customparams", config.user_path()) 44 | assert_equal("#{@system_dir}/.customparams", config.system_path()) 45 | end 46 | 47 | def test_custom_current_path 48 | config = Nugrant::Config.new({ 49 | :params_filename => ".customparams", 50 | :current_path => "#{@user_dir}/.currentcustomparams" 51 | }) 52 | 53 | assert_equal(".customparams", config.params_filename()) 54 | assert_equal("#{@user_dir}/.currentcustomparams", config.current_path()) 55 | end 56 | 57 | def test_custom_current_path_without_filename 58 | config = Nugrant::Config.new({ 59 | :params_filename => ".customparams", 60 | :current_path => "#{@user_dir}" 61 | }) 62 | 63 | assert_equal(".customparams", config.params_filename()) 64 | assert_equal("#{@user_dir}/.customparams", config.current_path()) 65 | end 66 | 67 | def test_custom_current_path_using_callable 68 | config = Nugrant::Config.new({ 69 | :params_filename => ".customparams", 70 | :current_path => lambda do || 71 | "#{@user_dir}/" 72 | end 73 | }) 74 | 75 | assert_equal(".customparams", config.params_filename()) 76 | assert_equal("#{@user_dir}/.customparams", config.current_path()) 77 | end 78 | 79 | def test_custom_user_path 80 | config = Nugrant::Config.new({ 81 | :params_filename => ".customparams", 82 | :user_path => "#{@system_dir}/.usercustomparams" 83 | }) 84 | 85 | assert_equal(".customparams", config.params_filename()) 86 | assert_equal("#{@system_dir}/.usercustomparams", config.user_path()) end 87 | 88 | def test_custom_system_path 89 | config = Nugrant::Config.new({ 90 | :params_filename => ".customparams", 91 | :system_path => "#{@current_dir}/.systemcustomparams" 92 | }) 93 | 94 | assert_equal(".customparams", config.params_filename()) 95 | assert_equal("#{@current_dir}/.systemcustomparams", config.system_path()) 96 | end 97 | 98 | def test_custom_all 99 | config = Nugrant::Config.new({ 100 | :params_filename => ".customparams", 101 | :current_path => "#{@user_dir}/.currentcustomparams", 102 | :user_path => "#{@system_dir}/.usercustomparams", 103 | :system_path => "#{@current_dir}/.systemcustomparams" 104 | }) 105 | 106 | assert_equal(".customparams", config.params_filename()) 107 | assert_equal("#{@user_dir}/.currentcustomparams", config.current_path()) 108 | assert_equal("#{@system_dir}/.usercustomparams", config.user_path()) 109 | assert_equal("#{@current_dir}/.systemcustomparams", config.system_path()) 110 | end 111 | 112 | def test_nil_current 113 | config = Nugrant::Config.new({ 114 | :params_filename => ".customparams", 115 | :current_path => nil, 116 | }) 117 | 118 | assert_equal("#{@current_dir}/.customparams", config.current_path()) 119 | end 120 | 121 | def test_nil_user 122 | config = Nugrant::Config.new({ 123 | :params_filename => ".customparams", 124 | :user_path => nil, 125 | }) 126 | 127 | assert_equal("#{@user_dir}/.customparams", config.user_path()) 128 | end 129 | 130 | def test_nil_system 131 | config = Nugrant::Config.new({ 132 | :params_filename => ".customparams", 133 | :system_path => nil, 134 | }) 135 | 136 | assert_equal("#{@system_dir}/.customparams", config.system_path()) 137 | end 138 | 139 | def test_invalid_format 140 | assert_raises(ArgumentError) do 141 | Nugrant::Config.new({:params_format => :invalid}) 142 | end 143 | end 144 | 145 | def test_merge 146 | config1 = Nugrant::Config.new({ 147 | :params_filename => ".customparams", 148 | :current_path => nil, 149 | }) 150 | 151 | config2 = Nugrant::Config.new({ 152 | :params_filename => ".overrideparams", 153 | :current_path => "something", 154 | }) 155 | 156 | config3 = config1.merge(config2) 157 | 158 | refute_same(config1, config3) 159 | refute_same(config2, config3) 160 | 161 | assert_equal(Nugrant::Config.new({ 162 | :params_filename => config2[:params_filename], 163 | :params_format => config2[:params_format], 164 | :current_path => config2[:current_path], 165 | :user_path => config2[:user_path], 166 | :system_path => config2[:system_path], 167 | :array_merge_strategy => config2[:array_merge_strategy], 168 | :key_error => config2[:key_error], 169 | :parse_error => config2[:parse_error], 170 | }), config3) 171 | end 172 | 173 | def test_merge! 174 | config1 = Nugrant::Config.new({ 175 | :params_filename => ".customparams", 176 | :current_path => nil, 177 | }) 178 | 179 | config2 = Nugrant::Config.new({ 180 | :params_filename => ".overrideparams", 181 | :current_path => "something", 182 | }) 183 | 184 | config3 = config1.merge!(config2) 185 | 186 | assert_same(config1, config3) 187 | refute_same(config2, config3) 188 | 189 | assert_equal(Nugrant::Config.new({ 190 | :params_filename => config2[:params_filename], 191 | :params_format => config2[:params_format], 192 | :current_path => config2[:current_path], 193 | :user_path => config2[:user_path], 194 | :system_path => config2[:system_path], 195 | :array_merge_strategy => config2[:array_merge_strategy], 196 | :key_error => config2[:key_error], 197 | :parse_error => config2[:parse_error], 198 | }), config3) 199 | end 200 | end 201 | end 202 | -------------------------------------------------------------------------------- /test/lib/nugrant/test_parameters.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'nugrant' 4 | require 'nugrant/helper/parameters' 5 | require 'nugrant/parameters' 6 | 7 | module Nugrant 8 | class TestParameters < ::Minitest::Test 9 | 10 | @@FORMATS = [:json, :yaml] 11 | @@INVALID_PATH = "impossible_file_path.yamljson.impossible" 12 | 13 | def test_params_level_1() 14 | formats.each do |format| 15 | parameters = create_parameters(format, "params_current_1", "params_user_1", "params_system_1") 16 | 17 | assert_level(parameters, { 18 | :'1.1.1' => "current", 19 | :'1.1.0' => "current", 20 | :'1.0.1' => "current", 21 | :'0.1.1' => "user", 22 | :'1.0.0' => "current", 23 | :'0.1.0' => "user", 24 | :'0.0.1' => "system", 25 | }) 26 | end 27 | end 28 | 29 | def test_params_level_2() 30 | formats.each do |format| 31 | parameters = create_parameters(format, "params_current_2", "params_user_2", "params_system_2") 32 | 33 | run_second_level(parameters, :'1.1.1', { 34 | :'1.1.1' => "current", 35 | :'1.1.0' => "current", 36 | :'1.0.1' => "current", 37 | :'0.1.1' => "user", 38 | :'1.0.0' => "current", 39 | :'0.1.0' => "user", 40 | :'0.0.1' => "system", 41 | }) 42 | 43 | run_second_level(parameters, :'1.1.0', { 44 | :'1.1.1' => "current", 45 | :'1.1.0' => "current", 46 | :'1.0.1' => "current", 47 | :'0.1.1' => "user", 48 | :'1.0.0' => "current", 49 | :'0.1.0' => "user", 50 | }) 51 | 52 | run_second_level(parameters, :'1.0.1', { 53 | :'1.1.1' => "current", 54 | :'1.1.0' => "current", 55 | :'1.0.1' => "current", 56 | :'0.1.1' => "system", 57 | :'1.0.0' => "current", 58 | :'0.0.1' => "system", 59 | }) 60 | 61 | run_second_level(parameters, :'0.1.1', { 62 | :'1.1.1' => "user", 63 | :'1.1.0' => "user", 64 | :'1.0.1' => "system", 65 | :'0.1.1' => "user", 66 | :'0.1.0' => "user", 67 | :'0.0.1' => "system", 68 | }) 69 | 70 | run_second_level(parameters, :'1.0.0', { 71 | :'1.1.1' => "current", 72 | :'1.1.0' => "current", 73 | :'1.0.1' => "current", 74 | :'1.0.0' => "current", 75 | }) 76 | 77 | run_second_level(parameters, :'0.1.0', { 78 | :'1.1.1' => "user", 79 | :'1.1.0' => "user", 80 | :'0.1.1' => "user", 81 | :'0.1.0' => "user", 82 | }) 83 | 84 | run_second_level(parameters, :'0.0.1', { 85 | :'1.1.1' => "system", 86 | :'1.0.1' => "system", 87 | :'0.1.1' => "system", 88 | :'0.0.1' => "system", 89 | }) 90 | 91 | assert_key_error(parameters, :'0.0.0') 92 | end 93 | end 94 | 95 | def run_second_level(parameters, key, results) 96 | assert_level(parameters.send(key), results) 97 | assert_level(parameters[key], results) 98 | 99 | assert_key_error(parameters, :'0.0.0') 100 | end 101 | 102 | def test_params_array() 103 | file_path = "params_array" 104 | formats.each do |format| 105 | parameters = create_parameters(format, file_path, invalid_path, invalid_path) 106 | 107 | assert_equal(["1", "2", "3"], parameters[:level1][:level2]) 108 | end 109 | end 110 | 111 | def test_file_nil() 112 | run_test_file_invalid(nil) 113 | end 114 | 115 | def test_file_does_not_exist() 116 | run_test_file_invalid("impossible_file_path.yml.impossible") 117 | end 118 | 119 | def run_test_file_invalid(invalid_value) 120 | formats.each do |format| 121 | parameters = create_parameters(format, "params_simple", invalid_path, invalid_path) 122 | assert_all_access_equal("value", parameters, "test") 123 | 124 | parameters = create_parameters(format, invalid_path, "params_simple", invalid_path) 125 | assert_all_access_equal("value", parameters, "test") 126 | 127 | parameters = create_parameters(format, invalid_path, invalid_path, "params_simple") 128 | assert_all_access_equal("value", parameters, "test") 129 | 130 | parameters = create_parameters(format, invalid_path, invalid_path, invalid_path) 131 | assert(parameters) 132 | end 133 | end 134 | 135 | def test_params_windows_eol() 136 | run_test_params_eol("params_windows_eol") 137 | end 138 | 139 | def test_params_unix_eol() 140 | run_test_params_eol("params_unix_eol") 141 | end 142 | 143 | def run_test_params_eol(file_path) 144 | formats.each do |format| 145 | parameters = create_parameters(format, file_path, invalid_path, invalid_path) 146 | 147 | assert_all_access_equal("value1", parameters, 'level1') 148 | assert_all_access_equal("value2", parameters['level2'], 'first') 149 | end 150 | end 151 | 152 | def test_restricted_defaults_usage() 153 | formats.each do |format| 154 | parameters = create_parameters(format, "params_defaults_at_root", invalid_path, invalid_path) 155 | 156 | assert_all_access_equal("value", parameters, :defaults) 157 | end 158 | 159 | formats.each do |format| 160 | parameters = create_parameters(format, "params_defaults_not_at_root", invalid_path, invalid_path) 161 | 162 | assert_all_access_equal("value", parameters.level, :defaults) 163 | end 164 | end 165 | 166 | def test_defaults() 167 | formats.each do |format| 168 | parameters = create_parameters(format, "params_simple", invalid_path, invalid_path) 169 | parameters.defaults = {:test => "override1", :level => "new1"} 170 | 171 | assert_all_access_equal("value", parameters, 'test') 172 | assert_all_access_equal("new1", parameters, 'level') 173 | end 174 | end 175 | 176 | def test_empty_file() 177 | formats.each do |format| 178 | parameters = create_parameters(format, "params_empty", invalid_path, invalid_path) 179 | parameters.defaults = {:test => "value"} 180 | 181 | assert_all_access_equal("value", parameters, 'test') 182 | end 183 | end 184 | 185 | def test_file_not_hash() 186 | ["boolean", "list"].each do |wrong_type| 187 | formats.each do |format| 188 | parameters = create_parameters(format, "params_#{wrong_type}", invalid_path, invalid_path) 189 | parameters.defaults = {:test => "value"} 190 | 191 | assert_all_access_equal("value", parameters, 'test') 192 | end 193 | end 194 | end 195 | 196 | def test_nil_values() 197 | formats.each do |format| 198 | parameters = create_parameters(format, "params_user_nil_values", invalid_path, invalid_path) 199 | parameters.defaults = {:nil => "Not nil", :deep => {:nil => "Not nil", :deeper => {:nil => "Not nil"}}} 200 | 201 | assert_all_access_equal("Not nil", parameters[:deep][:deeper], :nil) 202 | assert_all_access_equal("Not nil", parameters[:deep], :nil) 203 | assert_all_access_equal("Not nil", parameters, :nil) 204 | end 205 | 206 | formats.each do |format| 207 | parameters = create_parameters(format, "params_user_nil_values", invalid_path, invalid_path) 208 | 209 | assert_all_access_equal(nil, parameters[:deep][:deeper], :nil) 210 | assert_all_access_equal(nil, parameters[:deep], :nil) 211 | assert_all_access_equal(nil, parameters, :nil) 212 | end 213 | end 214 | 215 | def test_restricted_keys_are_still_accessible 216 | keys = Helper::Parameters.restricted_keys() 217 | elements = Hash[ 218 | keys.map do |key| 219 | [key, "#{key.to_s} - value"] 220 | end 221 | ] 222 | 223 | parameters = create_parameters(:json, invalid_path, invalid_path, invalid_path) 224 | parameters.defaults = elements 225 | 226 | keys.each do |key| 227 | assert_equal("#{key.to_s} - value", parameters[key.to_s], "parameters[#{key.to_s}]") 228 | assert_equal("#{key.to_s} - value", parameters[key.to_sym], "parameters[#{key.to_sym}]") 229 | end 230 | end 231 | 232 | def test_enumerable_method_insensitive() 233 | parameters = create_parameters(:json, "params_simple", invalid_path, invalid_path) 234 | parameters.defaults = {"test" => "override1", :level => :new1} 235 | 236 | assert_equal(1, parameters.count([:test, "value"])) 237 | assert_equal(1, parameters.count(["test", "value"])) 238 | assert_equal(0, parameters.count(["test"])) 239 | assert_equal(0, parameters.count([])) 240 | assert_equal(0, parameters.count(:a)) 241 | assert_equal(0, parameters.count(nil)) 242 | 243 | assert_equal(0, parameters.find_index([:test, "value"])) 244 | assert_equal(0, parameters.find_index(["test", "value"])) 245 | assert_equal(nil, parameters.find_index(["test"])) 246 | assert_equal(nil, parameters.find_index([])) 247 | assert_equal(nil, parameters.find_index(:a)) 248 | assert_equal(nil, parameters.find_index(nil)) 249 | assert_equal(0, parameters.find_index() { |key, value| key == :test and value == "value" }) 250 | 251 | assert_equal(false, parameters.include?([:test, "value"])) 252 | assert_equal(false, parameters.include?(["test", "value"])) 253 | end 254 | 255 | def test_hash_method_insensitive() 256 | parameters = create_parameters(:json, "params_simple", invalid_path, invalid_path) 257 | parameters.defaults = {"test" => "override1", :level => :new1} 258 | 259 | assert_equal([:test, "value"], parameters.assoc("test")) 260 | assert_equal([:test, "value"], parameters.assoc(:test)) 261 | 262 | # compare_by_identity ? 263 | 264 | parameters.delete("test") 265 | assert_equal(nil, parameters.assoc("test")) 266 | assert_equal(nil, parameters.assoc(:test)) 267 | 268 | parameters = create_parameters(:json, "params_simple", invalid_path, invalid_path) 269 | parameters.defaults = {"test" => "override1", :level => :new1} 270 | 271 | assert_equal([[:test, "value"], [:level, :new1]], parameters.collect {|key, value| [key, value]}) 272 | 273 | assert_equal("value", parameters.fetch("test")) 274 | assert_equal("value", parameters.fetch("test", "default")) 275 | assert_equal("default", parameters.fetch("unknown", "default")) 276 | 277 | assert_equal(true, parameters.has_key?("test")) 278 | assert_equal(true, parameters.has_key?(:test)) 279 | 280 | assert_equal(true, parameters.include?("test")) 281 | assert_equal(true, parameters.include?(:test)) 282 | 283 | assert_equal(true, parameters.member?("test")) 284 | assert_equal(true, parameters.member?(:test)) 285 | 286 | parameters.store("another", "different") 287 | assert_equal(true, parameters.member?("another")) 288 | assert_equal(true, parameters.member?(:another)) 289 | end 290 | 291 | def test_defaults_not_overwritten_on_array_merge_strategy_change 292 | parameters = create_parameters(:json, "params_array", invalid_path, invalid_path) 293 | parameters.defaults = {"level1" => {"level2" => ["4", "5", "6"]}} 294 | 295 | parameters.array_merge_strategy = :concat 296 | 297 | assert_equal(["4", "5", "6"], parameters.defaults().level1.level2) 298 | assert_equal(["1", "2", "3", "4", "5", "6"], parameters.level1.level2) 299 | end 300 | 301 | def test_merge() 302 | parameters1 = create_parameters(:json, "params_current_1", invalid_path, invalid_path, { 303 | "0.1.1" => "default", 304 | "0.1.0" => "default", 305 | "0.0.1" => "default", 306 | }) 307 | 308 | parameters2 = create_parameters(:json, "params_current_1", invalid_path, "params_system_1", { 309 | "0.1.0" => "default_overriden", 310 | }) 311 | 312 | parameters3 = parameters1.merge(parameters2) 313 | 314 | refute_same(parameters1, parameters3) 315 | refute_same(parameters2, parameters3) 316 | 317 | assert_equal(Nugrant::Parameters, parameters3.class) 318 | 319 | assert_level(parameters3, { 320 | :'1.1.1' => "current", 321 | :'1.1.0' => "current", 322 | :'1.0.1' => "current", 323 | :'0.1.1' => "system", 324 | :'1.0.0' => "current", 325 | :'0.1.0' => "default_overriden", 326 | :'0.0.1' => "system", 327 | }) 328 | end 329 | 330 | def test_merge!() 331 | parameters1 = create_parameters(:json, "params_current_1", invalid_path, invalid_path, { 332 | "0.1.1" => "default", 333 | "0.1.0" => "default", 334 | "0.0.1" => "default", 335 | }) 336 | 337 | parameters2 = create_parameters(:json, "params_current_1", invalid_path, "params_system_1", { 338 | "0.1.0" => "default_overriden", 339 | }) 340 | 341 | parameters3 = parameters1.merge!(parameters2) 342 | 343 | assert_same(parameters1, parameters3) 344 | refute_same(parameters2, parameters3) 345 | 346 | assert_equal(Nugrant::Parameters, parameters3.class) 347 | 348 | assert_level(parameters3, { 349 | :'1.1.1' => "current", 350 | :'1.1.0' => "current", 351 | :'1.0.1' => "current", 352 | :'0.1.1' => "system", 353 | :'1.0.0' => "current", 354 | :'0.1.0' => "default_overriden", 355 | :'0.0.1' => "system", 356 | }) 357 | end 358 | 359 | def test_merge_with_different_array_merge_strategy() 360 | parameters1 = create_parameters(:json, "params_array", invalid_path, invalid_path, { 361 | "level1" => { 362 | "level2" => ["3", "4", "5"] 363 | } 364 | }, :array_merge_strategy => :replace) 365 | 366 | parameters2 = create_parameters(:json, "params_array", invalid_path, invalid_path, { 367 | "level1" => { 368 | "level2" => ["3", "6", "7"] 369 | } 370 | }, :array_merge_strategy => :concat) 371 | 372 | parameters3 = parameters1.merge(parameters2) 373 | 374 | assert_equal(["1", "2", "3", "3", "6", "7"], parameters3.level1.level2) 375 | end 376 | 377 | def test_numeric_key_in_yaml_converted_to_symbol() 378 | parameters = create_parameters(:yaml, "params_numeric_key", invalid_path, invalid_path) 379 | 380 | assert_equal("value1", parameters.servers[:'1']) 381 | end 382 | 383 | ## Helpers & Assertions 384 | 385 | def create_parameters(format, current_filename, user_filename, system_filename, defaults = {}, options = {}) 386 | extension = case format 387 | when :json 388 | "json" 389 | when :yml, :yaml 390 | "yml" 391 | else 392 | raise ArgumentError, "Format [#{format}] is currently not supported" 393 | end 394 | 395 | resource_path = File.expand_path("#{File.dirname(__FILE__)}/../../resources/#{format}") 396 | 397 | current_path = "#{resource_path}/#{current_filename}.#{extension}" if current_filename 398 | user_path = "#{resource_path}/#{user_filename}.#{extension}" if user_filename 399 | system_path = "#{resource_path}/#{system_filename}.#{extension}" if system_filename 400 | 401 | return Nugrant::Parameters.new(defaults, { 402 | :format => format, 403 | :current_path => current_path, 404 | :user_path => user_path, 405 | :system_path => system_path, 406 | :array_merge_strategy => options[:array_merge_strategy] 407 | }) 408 | end 409 | 410 | def assert_all_access_equal(expected, parameters, key) 411 | assert_equal(expected, parameters.method_missing(key.to_sym), "parameters.#{key.to_s}") 412 | assert_equal(expected, parameters[key.to_s], "parameters[#{key.to_s}]") 413 | assert_equal(expected, parameters[key.to_sym], "parameters[#{key.to_sym}]") 414 | end 415 | 416 | def assert_level(parameters, results) 417 | results.each do |key, value| 418 | assert_all_access_equal(value, parameters, key) 419 | end 420 | 421 | assert_key_error(parameters, "0.0.0") 422 | end 423 | 424 | def assert_key_error(parameters, key) 425 | assert_raises(KeyError) do 426 | parameters[key] 427 | end 428 | end 429 | 430 | def formats() 431 | @@FORMATS 432 | end 433 | 434 | def invalid_path() 435 | @@INVALID_PATH 436 | end 437 | end 438 | end 439 | -------------------------------------------------------------------------------- /test/lib/test_helper.rb: -------------------------------------------------------------------------------- 1 | require "minitest/reporters" 2 | 3 | Minitest::Reporters.use! 4 | -------------------------------------------------------------------------------- /test/resources/.vagrantuser: -------------------------------------------------------------------------------- 1 | vm_port: 3332 2 | repository: 3 | personal: "/home/user/personal/git" 4 | -------------------------------------------------------------------------------- /test/resources/README.md: -------------------------------------------------------------------------------- 1 | This readme give information on how to read resources file 2 | that test merge possibilities. 3 | 4 | Naming conventions 5 | ----------------- 6 | 7 | The filename uses a specific convention: 8 | 9 | params_*kind*_*level*.[yml|json] 10 | 11 | The kind is one of: [`current`|`user`|`system`] and defines which 12 | responsibility they will hold. The order is `current` overrides 13 | `user` overrides `system`. 14 | 15 | Inside file, keys have special meaning. They define how 16 | the overrides take place. We used the binary format 17 | to represent each possibilities. 18 | 19 | key = "1.1.1" 20 | 21 | Each element represent a specific kind. Read from left to 22 | right, they are assigned to `current`, `user` and `system` 23 | respectively. 24 | 25 | So, 26 | 27 | `current` `user` `system` 28 | 1 . 1 . 1 29 | 30 | A 1 signify that the file kind *column* will have a key "1.1.1" set 31 | to value *kind*. A 0 means the key is not set. With this in mind, 32 | it is easy to reason about the value that will need to be asserted 33 | for key "1.1.1" on level file *level*. 34 | 35 | # params_current_1.yml file 36 | "1.1.1": "current" 37 | 38 | # params_user_1.yml file 39 | "1.1.1": "user" 40 | 41 | # params_system_1.yml file 42 | "1.1.1": "system" 43 | 44 | # Value to assert 45 | assert("current", parameters.get("1.1.1")) 46 | 47 | With the help of params_combinations, it is easy to create test file 48 | either for other formats or for more level. Copy the all keys expect 49 | "0.0.0" to the file. Say the file is of kind `current`, then for 50 | column `current`, each time a one is there, replace `base` by the 51 | kind (here `current`). The line that still have value base as the 52 | value must be deleted. 53 | -------------------------------------------------------------------------------- /test/resources/json/params_array.json: -------------------------------------------------------------------------------- 1 | { 2 | "level1": { 3 | "level2": ["1", "2", "3"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/resources/json/params_boolean.json: -------------------------------------------------------------------------------- 1 | false 2 | -------------------------------------------------------------------------------- /test/resources/json/params_combinations.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.1": "base", 3 | "1.1.0": "base", 4 | "1.0.1": "base", 5 | "0.1.1": "base", 6 | "1.0.0": "base", 7 | "0.1.0": "base", 8 | "0.0.1": "base", 9 | 10 | "1.1.1": { 11 | "1.1.1": "base", 12 | "1.1.0": "base", 13 | "1.0.1": "base", 14 | "0.1.1": "base", 15 | "1.0.0": "base", 16 | "0.1.0": "base", 17 | "0.0.1": "base" 18 | }, 19 | 20 | "1.1.0": { 21 | "1.1.1": "base", 22 | "1.1.0": "base", 23 | "1.0.1": "base", 24 | "0.1.1": "base", 25 | "1.0.0": "base", 26 | "0.1.0": "base", 27 | "0.0.1": "base" 28 | }, 29 | 30 | "1.0.1": { 31 | "1.1.1": "base", 32 | "1.1.0": "base", 33 | "1.0.1": "base", 34 | "0.1.1": "base", 35 | "1.0.0": "base", 36 | "0.1.0": "base", 37 | "0.0.1": "base" 38 | }, 39 | 40 | "0.1.1": { 41 | "1.1.1": "base", 42 | "1.1.0": "base", 43 | "1.0.1": "base", 44 | "0.1.1": "base", 45 | "1.0.0": "base", 46 | "0.1.0": "base", 47 | "0.0.1": "base" 48 | }, 49 | 50 | "1.0.0": { 51 | "1.1.1": "base", 52 | "1.1.0": "base", 53 | "1.0.1": "base", 54 | "0.1.1": "base", 55 | "1.0.0": "base", 56 | "0.1.0": "base", 57 | "0.0.1": "base" 58 | }, 59 | 60 | "0.1.0": { 61 | "1.1.1": "base", 62 | "1.1.0": "base", 63 | "1.0.1": "base", 64 | "0.1.1": "base", 65 | "1.0.0": "base", 66 | "0.1.0": "base", 67 | "0.0.1": "base" 68 | }, 69 | 70 | "0.0.1": { 71 | "1.1.1": "base", 72 | "1.1.0": "base", 73 | "1.0.1": "base", 74 | "0.1.1": "base", 75 | "1.0.0": "base", 76 | "0.1.0": "base", 77 | "0.0.1": "base" 78 | }, 79 | 80 | "0.0.0": "do not put, key should not exist" 81 | } 82 | -------------------------------------------------------------------------------- /test/resources/json/params_current_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.1": "current", 3 | "1.1.0": "current", 4 | "1.0.1": "current", 5 | "1.0.0": "current" 6 | } 7 | -------------------------------------------------------------------------------- /test/resources/json/params_current_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.1": { 3 | "1.1.1": "current", 4 | "1.1.0": "current", 5 | "1.0.1": "current", 6 | "1.0.0": "current" 7 | }, 8 | 9 | "1.1.0": { 10 | "1.1.1": "current", 11 | "1.1.0": "current", 12 | "1.0.1": "current", 13 | "1.0.0": "current" 14 | }, 15 | 16 | "1.0.1": { 17 | "1.1.1": "current", 18 | "1.1.0": "current", 19 | "1.0.1": "current", 20 | "1.0.0": "current" 21 | }, 22 | 23 | "1.0.0": { 24 | "1.1.1": "current", 25 | "1.1.0": "current", 26 | "1.0.1": "current", 27 | "1.0.0": "current" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/resources/json/params_defaults_at_root.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaults": "value" 3 | } 4 | -------------------------------------------------------------------------------- /test/resources/json/params_defaults_not_at_root.json: -------------------------------------------------------------------------------- 1 | { 2 | "level": { 3 | "defaults": "value" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/resources/json/params_empty.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maoueh/nugrant/6276b3e81364c6ed204d958ec01c653a34a74464/test/resources/json/params_empty.json -------------------------------------------------------------------------------- /test/resources/json/params_list.json: -------------------------------------------------------------------------------- 1 | ["value", "second"] 2 | -------------------------------------------------------------------------------- /test/resources/json/params_simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "value" 3 | } 4 | -------------------------------------------------------------------------------- /test/resources/json/params_system_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.1": "system", 3 | "1.0.1": "system", 4 | "0.1.1": "system", 5 | "0.0.1": "system" 6 | } 7 | -------------------------------------------------------------------------------- /test/resources/json/params_system_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.1": { 3 | "1.1.1": "system", 4 | "1.0.1": "system", 5 | "0.1.1": "system", 6 | "0.0.1": "system" 7 | }, 8 | 9 | "1.0.1": { 10 | "1.1.1": "system", 11 | "1.0.1": "system", 12 | "0.1.1": "system", 13 | "0.0.1": "system" 14 | }, 15 | 16 | "0.1.1": { 17 | "1.1.1": "system", 18 | "1.0.1": "system", 19 | "0.1.1": "system", 20 | "0.0.1": "system" 21 | }, 22 | 23 | "0.0.1": { 24 | "1.1.1": "system", 25 | "1.0.1": "system", 26 | "0.1.1": "system", 27 | "0.0.1": "system" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/resources/json/params_unix_eol.json: -------------------------------------------------------------------------------- 1 | { 2 | "level1": "value1", 3 | "level2": { 4 | "first": "value2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/resources/json/params_user_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.1": "user", 3 | "1.1.0": "user", 4 | "0.1.1": "user", 5 | "0.1.0": "user" 6 | } 7 | -------------------------------------------------------------------------------- /test/resources/json/params_user_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.1": { 3 | "1.1.1": "user", 4 | "1.1.0": "user", 5 | "0.1.1": "user", 6 | "0.1.0": "user" 7 | }, 8 | 9 | "1.1.0": { 10 | "1.1.1": "user", 11 | "1.1.0": "user", 12 | "0.1.1": "user", 13 | "0.1.0": "user" 14 | }, 15 | 16 | "0.1.1": { 17 | "1.1.1": "user", 18 | "1.1.0": "user", 19 | "0.1.1": "user", 20 | "0.1.0": "user" 21 | }, 22 | 23 | "0.1.0": { 24 | "1.1.1": "user", 25 | "1.1.0": "user", 26 | "0.1.1": "user", 27 | "0.1.0": "user" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/resources/json/params_user_nil_values.json: -------------------------------------------------------------------------------- 1 | { 2 | "nil": null, 3 | "deep": { 4 | "nil": null, 5 | "deeper": { 6 | "nil": null 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/resources/json/params_windows_eol.json: -------------------------------------------------------------------------------- 1 | { 2 | "level1": "value1", 3 | "level2": { 4 | "first": "value2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.auto_export: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.user.auto_export = [:autoenv, :script] 3 | 4 | config.user.defaults = { 5 | 'local' => { 6 | 'first' => "value1", 7 | }, 8 | } 9 | 10 | #config.vm.box = "opscode_centos-7.0_x86-64" 11 | 12 | raise "You need to define `config.vm.box` for this test" 13 | end 14 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.bag_inside_array: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.user.defaults = { 3 | nodes: [ 4 | {ip: '192.168.100.10'}, 5 | {ip: '192.168.100.11'} 6 | ] 7 | } 8 | 9 | config.user.nodes.each_with_index do |node, index| 10 | puts "Node ##{index}, string: #{node['ip']}" 11 | puts "Node ##{index}, symbol: #{node[:ip]}" 12 | puts "Node ##{index}, method: #{node.ip}" 13 | puts "" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.defaults_mixed_string_symbols: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.user.defaults = { 3 | 'single' => 1, 4 | :local => { 5 | 'first' => "value1", 6 | :second => "value2" 7 | } 8 | } 9 | 10 | puts config.user.single 11 | puts config.user[:single] 12 | 13 | puts config.user.local.first 14 | puts config.user[:local]["first"] 15 | 16 | puts config.user.local.second 17 | puts config.user["local"][:second] 18 | end 19 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.defaults_null_values_in_vagrantuser: -------------------------------------------------------------------------------- 1 | # Source from https://github.com/maoueh/nugrant/issues/12#issuecomment-27054150 2 | # but slightly modified 3 | Vagrant.configure('2') do |config| 4 | config.user.defaults = { 5 | "host" => { 6 | "rails_server_port" => 3000, 7 | "apps_folder" => "../../rails-apps/" }, 8 | "plugins" => { 9 | "vagrant_cachier" => { 10 | "enabled" => false 11 | } 12 | } 13 | } 14 | 15 | puts "Host Rails Server Port: #{config.user.host.rails_server_port}" 16 | puts "Host App Folder: #{config.user.host.apps_folder}" 17 | puts "Gitconfig Name: #{config.user.gitconfig.name}" 18 | puts "Gitconfig Email: #{config.user.gitconfig.email}" 19 | 20 | if config.user.plugins.vagrant_cachier.enabled 21 | puts "Vagrant Cachier is Enabled!" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.defaults_using_string: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.user.defaults = { 3 | 'single' => 1, 4 | 'local' => { 5 | 'first' => "value1", 6 | 'second' => "value2" 7 | } 8 | } 9 | 10 | puts config.user.single 11 | puts config.user[:single] 12 | 13 | puts config.user.local.first 14 | puts config.user[:local]["first"] 15 | 16 | puts config.user.local.second 17 | puts config.user["local"][:second] 18 | end 19 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.defaults_using_symbol: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.user.defaults = { 3 | :single => 1, 4 | :local => { 5 | :first => "value1", 6 | :second => "value2" 7 | } 8 | } 9 | 10 | puts config.user.single 11 | puts config.user[:single] 12 | 13 | puts config.user.local.first 14 | puts config.user[:local]["first"] 15 | 16 | puts config.user.local.second 17 | puts config.user["local"][:second] 18 | end 19 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.empty: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | end 3 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.fake: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.user.defaults = { 3 | 'local' => { 4 | 'first' => "value1", 5 | }, 6 | 'array' => { 7 | 'level1' => [1, 2] 8 | }, 9 | 'application' => { 10 | 'users' => { 11 | "joe" => { 12 | :full => "Joe Plumber" 13 | }, 14 | "jane" => { 15 | :full => "Jane Cook" 16 | }, 17 | } 18 | } 19 | } 20 | 21 | config.user.application.users.each do |key, data| 22 | puts "Key #{key}: #{data}" 23 | end 24 | 25 | config.user.array_merge_strategy = :concat 26 | config.user.auto_export = [:autoenv, :script] 27 | 28 | config.vm.box = "opscode_centos-7.0_x86-64" 29 | end 30 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.missing_parameter: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | puts config.user.unkown_for_sure 3 | end 4 | -------------------------------------------------------------------------------- /test/resources/vagrantfiles/v2.real: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "base" 3 | config.vm.box_url = "http://domain.com/path/to/above.box" 4 | 5 | config.vm.network :forwarded_port, guest: 80, host: 8080 6 | config.vm.network :private_network, ip: "10.0.0.2" 7 | 8 | config.vm.synced_folder "C:/Users/Matt/nugrant", "/vagrant_data" 9 | 10 | config.vm.provider :virtualbox do |virtualbox| 11 | virtualbox.customize ["modifyvm", :id, "--memory", "256"] 12 | end 13 | 14 | config.vm.provision :chef_solo do |chef| 15 | chef.cookbooks_path = "C:\Users\Matt\Work\kitchen\cookbooks" 16 | chef.roles_path = "C:\Users\Matt\Work\kitchen\roles" 17 | chef.nodes_path = "C:\Users\Matt\Work\kitchen\nodes" 18 | 19 | chef.add_recipe "mysql" 20 | chef.add_role "web" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/resources/yaml/params_array.yml: -------------------------------------------------------------------------------- 1 | level1: 2 | level2: 3 | - "1" 4 | - "2" 5 | - "3" 6 | -------------------------------------------------------------------------------- /test/resources/yaml/params_boolean.yml: -------------------------------------------------------------------------------- 1 | false 2 | -------------------------------------------------------------------------------- /test/resources/yaml/params_combinations.yml: -------------------------------------------------------------------------------- 1 | "1.1.1": "base" 2 | "1.1.0": "base" 3 | "1.0.1": "base" 4 | "0.1.1": "base" 5 | "1.0.0": "base" 6 | "0.1.0": "base" 7 | "0.0.1": "base" 8 | 9 | "1.1.1": 10 | "1.1.1": "base" 11 | "1.1.0": "base" 12 | "1.0.1": "base" 13 | "0.1.1": "base" 14 | "1.0.0": "base" 15 | "0.1.0": "base" 16 | "0.0.1": "base" 17 | 18 | "1.1.0": 19 | "1.1.1": "base" 20 | "1.1.0": "base" 21 | "1.0.1": "base" 22 | "0.1.1": "base" 23 | "1.0.0": "base" 24 | "0.1.0": "base" 25 | "0.0.1": "base" 26 | 27 | "1.0.1": 28 | "1.1.1": "base" 29 | "1.1.0": "base" 30 | "1.0.1": "base" 31 | "0.1.1": "base" 32 | "1.0.0": "base" 33 | "0.1.0": "base" 34 | "0.0.1": "base" 35 | 36 | "0.1.1": 37 | "1.1.1": "base" 38 | "1.1.0": "base" 39 | "1.0.1": "base" 40 | "0.1.1": "base" 41 | "1.0.0": "base" 42 | "0.1.0": "base" 43 | "0.0.1": "base" 44 | 45 | "1.0.0": 46 | "1.1.1": "base" 47 | "1.1.0": "base" 48 | "1.0.1": "base" 49 | "0.1.1": "base" 50 | "1.0.0": "base" 51 | "0.1.0": "base" 52 | "0.0.1": "base" 53 | 54 | "0.1.0": 55 | "1.1.1": "base" 56 | "1.1.0": "base" 57 | "1.0.1": "base" 58 | "0.1.1": "base" 59 | "1.0.0": "base" 60 | "0.1.0": "base" 61 | "0.0.1": "base" 62 | 63 | "0.0.1": 64 | "1.1.1": "base" 65 | "1.1.0": "base" 66 | "1.0.1": "base" 67 | "0.1.1": "base" 68 | "1.0.0": "base" 69 | "0.1.0": "base" 70 | "0.0.1": "base" 71 | 72 | "0.0.0": "do not put key should not exist" 73 | -------------------------------------------------------------------------------- /test/resources/yaml/params_current_1.yml: -------------------------------------------------------------------------------- 1 | "1.1.1": "current" 2 | "1.1.0": "current" 3 | "1.0.1": "current" 4 | "1.0.0": "current" 5 | -------------------------------------------------------------------------------- /test/resources/yaml/params_current_2.yml: -------------------------------------------------------------------------------- 1 | "1.1.1": 2 | "1.1.1": "current" 3 | "1.1.0": "current" 4 | "1.0.1": "current" 5 | "1.0.0": "current" 6 | 7 | "1.1.0": 8 | "1.1.1": "current" 9 | "1.1.0": "current" 10 | "1.0.1": "current" 11 | "1.0.0": "current" 12 | 13 | "1.0.1": 14 | "1.1.1": "current" 15 | "1.1.0": "current" 16 | "1.0.1": "current" 17 | "1.0.0": "current" 18 | 19 | "1.0.0": 20 | "1.1.1": "current" 21 | "1.1.0": "current" 22 | "1.0.1": "current" 23 | "1.0.0": "current" 24 | -------------------------------------------------------------------------------- /test/resources/yaml/params_defaults_at_root.yml: -------------------------------------------------------------------------------- 1 | "defaults": "value" 2 | -------------------------------------------------------------------------------- /test/resources/yaml/params_defaults_not_at_root.yml: -------------------------------------------------------------------------------- 1 | "level": 2 | "defaults": "value" 3 | -------------------------------------------------------------------------------- /test/resources/yaml/params_empty.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maoueh/nugrant/6276b3e81364c6ed204d958ec01c653a34a74464/test/resources/yaml/params_empty.yml -------------------------------------------------------------------------------- /test/resources/yaml/params_list.yml: -------------------------------------------------------------------------------- 1 | - first 2 | - second 3 | -------------------------------------------------------------------------------- /test/resources/yaml/params_numeric_key.yml: -------------------------------------------------------------------------------- 1 | servers: 2 | 1: "value1" 3 | 2: "value2" 4 | -------------------------------------------------------------------------------- /test/resources/yaml/params_simple.yml: -------------------------------------------------------------------------------- 1 | test: "value" 2 | -------------------------------------------------------------------------------- /test/resources/yaml/params_system_1.yml: -------------------------------------------------------------------------------- 1 | "1.1.1": "system" 2 | "1.0.1": "system" 3 | "0.1.1": "system" 4 | "0.0.1": "system" 5 | -------------------------------------------------------------------------------- /test/resources/yaml/params_system_2.yml: -------------------------------------------------------------------------------- 1 | "1.1.1": 2 | "1.1.1": "system" 3 | "1.0.1": "system" 4 | "0.1.1": "system" 5 | "0.0.1": "system" 6 | 7 | "1.0.1": 8 | "1.1.1": "system" 9 | "1.0.1": "system" 10 | "0.1.1": "system" 11 | "0.0.1": "system" 12 | 13 | "0.1.1": 14 | "1.1.1": "system" 15 | "1.0.1": "system" 16 | "0.1.1": "system" 17 | "0.0.1": "system" 18 | 19 | "0.0.1": 20 | "1.1.1": "system" 21 | "1.0.1": "system" 22 | "0.1.1": "system" 23 | "0.0.1": "system" 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/resources/yaml/params_unix_eol.yml: -------------------------------------------------------------------------------- 1 | level1: "value1" 2 | level2: 3 | first: "value2" 4 | -------------------------------------------------------------------------------- /test/resources/yaml/params_user_1.yml: -------------------------------------------------------------------------------- 1 | "1.1.1": "user" 2 | "1.1.0": "user" 3 | "0.1.1": "user" 4 | "0.1.0": "user" 5 | -------------------------------------------------------------------------------- /test/resources/yaml/params_user_2.yml: -------------------------------------------------------------------------------- 1 | "1.1.1": 2 | "1.1.1": "user" 3 | "1.1.0": "user" 4 | "0.1.1": "user" 5 | "0.1.0": "user" 6 | 7 | "1.1.0": 8 | "1.1.1": "user" 9 | "1.1.0": "user" 10 | "0.1.1": "user" 11 | "0.1.0": "user" 12 | 13 | "0.1.1": 14 | "1.1.1": "user" 15 | "1.1.0": "user" 16 | "0.1.1": "user" 17 | "0.1.0": "user" 18 | 19 | "0.1.0": 20 | "1.1.1": "user" 21 | "1.1.0": "user" 22 | "0.1.1": "user" 23 | "0.1.0": "user" 24 | -------------------------------------------------------------------------------- /test/resources/yaml/params_user_nil_values.yml: -------------------------------------------------------------------------------- 1 | nil: 2 | deep: 3 | nil: 4 | deeper: 5 | nil: 6 | -------------------------------------------------------------------------------- /test/resources/yaml/params_windows_eol.yml: -------------------------------------------------------------------------------- 1 | level1: "value1" 2 | level2: 3 | first: "value2" 4 | --------------------------------------------------------------------------------