lapis_lazuli 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -1
  3. data/lapis_lazuli.gemspec +10 -8
  4. data/lib/lapis_lazuli/api.rb +1 -1
  5. data/lib/lapis_lazuli/argparse.rb +1 -1
  6. data/lib/lapis_lazuli/browser.rb +37 -61
  7. data/lib/lapis_lazuli/browser/error.rb +89 -62
  8. data/lib/lapis_lazuli/browser/find.rb +1 -2
  9. data/lib/lapis_lazuli/cli.rb +1 -1
  10. data/lib/lapis_lazuli/cucumber.rb +1 -1
  11. data/lib/lapis_lazuli/generators/cucumber.rb +1 -1
  12. data/lib/lapis_lazuli/generators/cucumber/template/README.md +2 -0
  13. data/lib/lapis_lazuli/generators/cucumber/template/config/config.yml +6 -21
  14. data/lib/lapis_lazuli/generators/cucumber/template/config/cucumber.yml +42 -13
  15. data/lib/lapis_lazuli/generators/cucumber/template/config/users.yml +21 -0
  16. data/lib/lapis_lazuli/generators/cucumber/template/features/1_basic.feature +49 -0
  17. data/lib/lapis_lazuli/generators/cucumber/template/features/2_account.feature +38 -0
  18. data/lib/lapis_lazuli/generators/cucumber/template/features/3_todo_list.feature +23 -0
  19. data/lib/lapis_lazuli/generators/cucumber/template/features/helpers/authentication_helper.rb +122 -0
  20. data/lib/lapis_lazuli/generators/cucumber/template/features/helpers/navigation_helper.rb +64 -0
  21. data/lib/lapis_lazuli/generators/cucumber/template/features/helpers/registration_helper.rb +102 -0
  22. data/lib/lapis_lazuli/generators/cucumber/template/features/helpers/user_helper.rb +74 -0
  23. data/lib/lapis_lazuli/generators/cucumber/template/features/step_definitions/account_steps.rb +60 -0
  24. data/lib/lapis_lazuli/generators/cucumber/template/features/step_definitions/basic_steps.rb +70 -0
  25. data/lib/lapis_lazuli/generators/cucumber/template/features/step_definitions/todo_steps.rb +27 -0
  26. data/lib/lapis_lazuli/generators/cucumber/template/features/support/env.rb +3 -2
  27. data/lib/lapis_lazuli/generic/xpath.rb +1 -1
  28. data/lib/lapis_lazuli/options.rb +3 -2
  29. data/lib/lapis_lazuli/placeholders.rb +1 -1
  30. data/lib/lapis_lazuli/proxy.rb +1 -1
  31. data/lib/lapis_lazuli/runtime.rb +1 -1
  32. data/lib/lapis_lazuli/scenario.rb +1 -1
  33. data/lib/lapis_lazuli/storage.rb +1 -1
  34. data/lib/lapis_lazuli/version.rb +2 -2
  35. data/lib/lapis_lazuli/versions.rb +1 -1
  36. data/lib/lapis_lazuli/world/config.rb +348 -334
  37. data/lib/lapis_lazuli/world/hooks.rb +85 -84
  38. data/lib/lapis_lazuli/world/logging.rb +1 -1
  39. data/test/Gemfile +2 -16
  40. data/test/config/config.yml +7 -6
  41. data/test/config/cucumber.yml +6 -8
  42. data/test/features/bindings.feature +1 -1
  43. data/test/features/browser.feature +1 -1
  44. data/test/features/step_definitions/interaction_steps.rb +5 -2
  45. data/test/features/step_definitions/validation_steps.rb +2 -2
  46. data/test/features/support/env.rb +21 -1
  47. data/test/results/latest_results.json +0 -0
  48. metadata +74 -28
  49. data/lib/lapis_lazuli/generators/cucumber/template/features/account.feature +0 -26
  50. data/lib/lapis_lazuli/generators/cucumber/template/features/example.feature +0 -30
  51. data/lib/lapis_lazuli/generators/cucumber/template/features/step_definitions/interaction_steps.rb +0 -165
  52. data/lib/lapis_lazuli/generators/cucumber/template/features/step_definitions/precondition_steps.rb +0 -63
  53. data/lib/lapis_lazuli/generators/cucumber/template/features/step_definitions/validation_steps.rb +0 -67
  54. data/lib/lapis_lazuli/generators/cucumber/template/features/support/functions.rb +0 -68
@@ -0,0 +1,74 @@
1
+ # This helper loads user data from the config files.
2
+ # After loading the data, it will overwrite certain strings, like __TIMESTAMP__ to randomize information
3
+ module User
4
+ extend LapisLazuli
5
+
6
+ class << self
7
+ @@data = nil
8
+
9
+ def load_user_data(user)
10
+ data = config('users.default-user')
11
+ begin
12
+ specific_data = config("users.#{user}")
13
+ rescue Exception => err1
14
+ begin
15
+ specific_data = config("users.#{ENV['TEST_ENV']}.#{user}")
16
+ rescue Exception => err2
17
+ error "The given user `#{user}` was not found in any of the config files:\n- #{err1.message}\n- #{err2.message}"
18
+ end
19
+ end
20
+ new_data = data.merge specific_data
21
+ @@data = replace_hash_constants(new_data)
22
+ end
23
+
24
+ def get(field)
25
+ return @@data[field]
26
+ end
27
+
28
+ def set(field, value)
29
+ @@data[field] = User.replace_constants(value)
30
+ end
31
+
32
+ # Replace random or time values of a complete hash
33
+ def replace_hash_constants(hash)
34
+ if hash.respond_to? :each
35
+ new_hash = {}
36
+ hash.each do |key, value|
37
+ new_hash[key] = replace_constants(value)
38
+ end
39
+ else
40
+ new_hash = replace_constants(hash)
41
+ end
42
+ return new_hash
43
+ end
44
+
45
+ # replace certain constants in a string, for example '_TIMESTAMP_' becomes '154875631'
46
+ def replace_constants(value)
47
+ if value.to_s == value
48
+ epoch = Time.now.to_i
49
+ alpha = number_to_letter(epoch)
50
+ timestamp = Time.now.strftime("D%Y-%M-%d-T%H-%M-%S")
51
+
52
+ old_val = value.to_s
53
+ value = value.sub('_RAND_', epoch.to_s)
54
+ value = value.sub('_TIMESTAMP_', timestamp)
55
+ value = value.sub('_RAND-ALPHA_', alpha)
56
+ unless value == old_val
57
+ log.debug "#{old_val} > #{value}"
58
+ end
59
+ end
60
+ return value
61
+ end
62
+
63
+ def number_to_letter(numbers)
64
+ num_string = numbers.to_s
65
+ alpha26 = ("a".."j").to_a
66
+ letters = ''
67
+ num_string.scan(/./).each do |number|
68
+ letters += alpha26[number.to_i]
69
+ end
70
+ return letters
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,60 @@
1
+ # A step definition is a regex, to learn more about this go to http://rubular.com/
2
+ # More info: https://github.com/cucumber/cucumber/wiki/Step-Definitions
3
+
4
+ # The following step definition accepts both:
5
+ # - the user logs in > will use the last stored user data
6
+ # - "user-x" logs in > will load user data from config.yml
7
+ When(/^"?(.*?)"? logs in$/) do |user|
8
+ user = nil if user == 'the user'
9
+ # Check out ./features/helpers/ for the function being called
10
+ Auth.log_in(user)
11
+ end
12
+
13
+ When(/^the user clicks on the logout button$/) do
14
+ Auth.log_out
15
+ end
16
+
17
+ Given(/^the user is logged out$/) do
18
+ Auth.ensure_log_out
19
+ end
20
+
21
+ Given(/^"(.*?)" is logged in$/) do |user|
22
+ Auth.ensure_log_in(user)
23
+ end
24
+
25
+ # One step definition that handles both the logged in as the logged out state
26
+ Then(/^the page should display as logged (in|out) state$/) do |logged|
27
+ # Adjust variable for checking logged in or logged out state.
28
+ if logged == 'in' and !Auth.is_logged_in?
29
+ error 'Unable to find profile picture, the user wasnt logged in successfully'
30
+ elsif logged == 'out' and Auth.is_logged_in?
31
+ error 'The profile picture is present, indicating that the user did not log out successfully'
32
+ end
33
+ end
34
+
35
+ # A static way to write your step definition
36
+ When 'the user clicks on the registration button' do
37
+ Register.open_registration
38
+ end
39
+
40
+ When 'the registration form should display' do
41
+ error 'The registration form did not display.' unless Register.is_registration_open?
42
+ end
43
+
44
+ Given /^"(.*?)" has the registration form opened$/ do |user|
45
+ User.load_user_data(user)
46
+ Register.ensure_open_registrarion
47
+ end
48
+
49
+ Given /^"(.*?)" has registered a new account$/ do |user|
50
+ Register.ensure_registered(user)
51
+ end
52
+
53
+ When 'the user completes registration' do
54
+ Register.register_user
55
+ end
56
+
57
+ Then 'the successful registration message should display' do
58
+ result, message = Register.registration_result
59
+ error message unless result
60
+ end
@@ -0,0 +1,70 @@
1
+ ################################################################################
2
+ # Copyright <%= config[:year] %> spriteCloud B.V. All rights reserved.
3
+ # Generated by LapisLazuli, version <%= config[:lapis_lazuli][:version] %>
4
+ # Author: "<%= config[:user] %>" <<%= config[:email] %>>
5
+
6
+ # interactions_steps.rb is used to interact with elements on the page.
7
+ # Quite advances piece of regex. Goto http://rubular.com/ for practise
8
+ # Whatever is put between parenthesis is captured as a variable unless you start with `?:`
9
+ # /(?: text)?/ means: ` text` is optional and do not capture it as a variable
10
+ # /(.*?)/ means: Any amount of any character, but don't be "greedy"
11
+ # "greedy" means, do not take characters that match the rest of the regex.
12
+ Given(/^the user navigates to (?:the )?"(.*?)"(?: page)?$/) do |page|
13
+ Nav.to(page)
14
+ end
15
+
16
+ # An example of interacting with some elements
17
+ Given(/^the user searches for "(.*?)"$/) do |query|
18
+ # Get the input element
19
+ searchbox = browser.find(:text_field => {:name => "s"})
20
+ # Make sure the input field is empty
21
+ searchbox.clear rescue log.debug "Could not clear searchbox"
22
+ # Fill in the query
23
+ searchbox.send_keys(query)
24
+ # Press enter to submit the search
25
+ searchbox.send_keys(:enter)
26
+ end
27
+
28
+ When(/^the user scrolls down$/) do
29
+ browser.driver.execute_script("window.scrollBy(0,400)")
30
+ end
31
+
32
+ Then(/^text "([^"]*)" should display somewhere on the page$/) do |string|
33
+ # Search for the text on the page
34
+ browser.wait(:xpath => "//*[contains(text(),\"#{string}\")]")
35
+ end
36
+
37
+ When(/^the user clicks on link "(.*?)"$/) do |url|
38
+ # Search for the element that includes the expected text
39
+
40
+ browser.wait(
41
+ :like => {
42
+ :element => :a,
43
+ :attribute => :href,
44
+ :include => url
45
+ }
46
+ ).click
47
+ end
48
+
49
+ When(/^the user clicks on the spritecloud logo$/) do
50
+ # Search for the logo
51
+ logo = browser.find(
52
+ a: {class: ['logo']},
53
+ message: 'Unable to find the logo on this page.'
54
+ )
55
+ # And click the logo
56
+ logo.click
57
+ end
58
+
59
+ Then(/^the user should be on page "(.*?)"$/) do |page|
60
+ url = Nav.set_url page
61
+ Nav.wait_for_url url
62
+ end
63
+
64
+ Then /^the text "(.*?)" should display on the blog page$/ do |expected_text|
65
+ # Many things wrong here, can you fix it?
66
+ header = browser.find(:like => [:h2, :id, 'entry_title'])
67
+ unless heeder.text.include? expected_text
68
+ error "Unable to find text `#{expected_text}`"
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ When(/^a todo item with text "(.*?)" is added$/) do |string|
2
+ pending # Write code here that turns the phrase above into concrete actions
3
+ end
4
+
5
+ Then(/^a todo item with text "(.*?)" should be present$/) do |string|
6
+ pending # Write code here that turns the phrase above into concrete actions
7
+ end
8
+
9
+ Given(/^"(.*?)" has (at least|exactly|at most) (\d+) todo items?$/) do |user, rule, number|
10
+ pending # Write code here that turns the phrase above into concrete actions
11
+ end
12
+
13
+ When(/^the user marks (\d+|all) todo items as completed$/) do |amount|
14
+ pending # Write code here that turns the phrase above into concrete actions
15
+ end
16
+
17
+ When("the clear completed button is pressed") do
18
+ pending # Write code here that turns the phrase above into concrete actions
19
+ end
20
+
21
+ Then("no todo items should display") do
22
+ pending # Write code here that turns the phrase above into concrete actions
23
+ end
24
+
25
+ Then("the progress bar should display at {int} percent") do |int|
26
+ pending # Write code here that turns the phrase above into concrete actions
27
+ end
@@ -5,13 +5,14 @@
5
5
  require 'lapis_lazuli'
6
6
  require 'lapis_lazuli/cucumber'
7
7
 
8
- LapisLazuli::WorldModule::Config.config_file = "config/config.yml"
8
+ LapisLazuli::WorldModule::Config.add_config("config/config.yml")
9
+ LapisLazuli::WorldModule::Config.add_config("config/users.yml")
9
10
  World(LapisLazuli)
10
11
 
11
12
  # Do something when LapisLazuli is started (This is before the browser is opened)
12
13
  LapisLazuli.Start do
13
14
  #If BROWSER is NIL, Lapis Lazuli will default to Firefox
14
- if !ENV['BROWSER'] || ENV['BROWSER'] == 'firefox'
15
+ if ENV['BROWSER'] == 'firefox'
15
16
 
16
17
  # Get Selenium to create a profile object
17
18
  require 'selenium-webdriver'
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  module LapisLazuli
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  module LapisLazuli
@@ -13,10 +13,11 @@ module LapisLazuli
13
13
  "error_strings" => [nil, "List of strings that indicate errors when detected on a web page."],
14
14
  "default_env" => [nil, "Indicates which environment specific configuration to load when no test environment is provided explicitly."],
15
15
  "test_env" => [nil, "Indicates which environment specific configuration to load in this test run."],
16
- "browser" => ['firefox', "Indicates the browser in which to run tests. Possible values are 'firefox', 'chrome', 'safari', 'ie', 'ios'."],
16
+ "browser" => [nil, "Indicates the browser in which to run tests. Possible values are 'firefox', 'chrome', 'safari', 'ie', 'ios'."],
17
17
  "email_domain" => ["google.com", "The domain name used when generating email addresses. See the `placeholders` command for more information."],
18
18
  "screenshot_on_failure" => [true, "Toggle whether failed scenarios should result in a screenshot being taken automatically."],
19
19
  "screenshot_dir" => [".#{File::SEPARATOR}screenshots", "Location prefix for the screenshot path."],
20
+ "screenshots_height" => [nil, "When 'full' the window will be resized to max height before taking a screenshot"],
20
21
  "screenshot_scheme" => ["old", "Naming scheme for screenshots. Possible values are 'old' and 'new'. This option will be deprecated in the near future, and only the new scheme will be supported."],
21
22
  "breakpoint_on_error" => [false, "If the error() function is used to create errors, should the debugger be started?"],
22
23
  "step_pause_time" => [0, "(Deprecated) Number of seconds to wait after each cucumber step is executed."],
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  #
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
 
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  require 'singleton'
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  require "securerandom"
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  module LapisLazuli
@@ -2,9 +2,9 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  module LapisLazuli
9
- VERSION = "2.0.1"
9
+ VERSION = "3.0.0"
10
10
  end
@@ -2,7 +2,7 @@
2
2
  # LapisLazuli
3
3
  # https://github.com/spriteCloud/lapis-lazuli
4
4
  #
5
- # Copyright (c) 2013-2017 spriteCloud B.V. and other LapisLazuli contributors.
5
+ # Copyright (c) 2013-2019 spriteCloud B.V. and other LapisLazuli contributors.
6
6
  # All rights reserved.
7
7
  #
8
8
  require 'lapis_lazuli/api'
@@ -9,193 +9,204 @@
9
9
  require "lapis_lazuli/options"
10
10
  require "lapis_lazuli/storage"
11
11
  require "lapis_lazuli/runtime"
12
+ require 'deep_merge'
12
13
 
13
14
  module LapisLazuli
14
- module WorldModule
15
- ##
16
- # Module with configuration loading related functions
17
- #
18
- # Manages the following:
19
- # @config - internal configuration representation
20
- # config_file - Needs to be set before config can be accessed.
21
- # @env - loaded/detected config/test environment
22
- module Config
15
+ module WorldModule
23
16
  ##
24
- # Explicitly store the configuration file name.
25
- module ClassMethods
26
- def config_file=(name)
27
- @config_file = name
28
- end
17
+ # Module with configuration loading related functions
18
+ #
19
+ # Manages the following:
20
+ # @config - internal configuration representation
21
+ # config_files - Needs to be set before config can be accessed.
22
+ # @env - loaded/detected config/test environment
23
+ module Config
24
+ ##
25
+ # Explicitly store the configuration file name.
26
+ module ClassMethods
27
+
28
+ # <b>DEPRECATED:</b> Please use <tt>add_config</tt> instead.
29
+ def config_file=(name)
30
+ warn "[DEPRECATION] `config_file = name` is deprecated. Please use `add_config(file)` instead."
31
+ add_config(name)
32
+ end
29
33
 
30
- def config_file
31
- return @config_file || "config/config.yml"
32
- end
33
- end
34
- extend ClassMethods
34
+ # <b>DEPRECATED:</b> Please use <tt>config_files</tt> instead.
35
+ def config_file
36
+ warn "[DEPRECATION] `config_file` is deprecated. Please use `config_files` instead."
37
+ return config_files
38
+ end
35
39
 
40
+ def add_config(file)
41
+ @config_files = [] if @config_files.nil?
42
+ @config_files.push(file)
43
+ end
36
44
 
37
- ##
38
- # The configuration is not a singleton, precisely, but it does not need to
39
- # be created more than once. Note that explicitly calling load_config will
40
- # still work to overwrite an existing configuration.
41
- def init
42
- # Guard against doing this more than once.
43
- if not @config.nil?
44
- return
45
- end
46
-
47
- if Config.config_file.nil?
48
- raise "No configuration file provided, set LapisLazuli::WorldModule::Config.config_file"
45
+ def config_files
46
+ return @config_files || ["config/config.yml"]
47
+ end
49
48
  end
49
+ extend ClassMethods
50
50
 
51
- load_config(Config.config_file)
52
- # In case there was no config file found an empty @config needs to be set to prevent infinite looping.
53
- if @config.nil?
54
- warn 'Unable to find a configuration file, defaulting to empty config.yml.'
55
- @config = {}
56
- end
57
-
58
- @metadata = Runtime.instance.set_if(self, :metadata) do
59
- log.debug "Creating metadata storage"
60
- Storage.new("metadata")
61
- end
62
- end
63
51
 
64
- def metadata
65
- if @metadata.nil?
66
- raise "No metadata available"
67
- end
68
- return @metadata
69
- end
52
+ ##
53
+ # The configuration is not a singleton, precisely, but it does not need to
54
+ # be created more than once. Note that explicitly calling load_config will
55
+ # still work to overwrite an existing configuration.
56
+ def init
57
+ # Guard against doing this more than once.
58
+ unless @config.nil?
59
+ return
60
+ end
61
+
62
+ if Config.config_files.nil?
63
+ raise "No configuration file provided, set LapisLazuli::WorldModule::Config.config_files"
64
+ end
70
65
 
66
+ load_config(Config.config_files)
67
+ # In case there was no config file found an empty @config needs to be set to prevent infinite looping.
68
+ if @config.nil?
69
+ warn 'Unable to find a configuration file, defaulting to empty config.yml.'
70
+ @config = {}
71
+ end
71
72
 
72
- ##
73
- # Loads a config based on a filename
74
- #
75
- # Supports: YML, JSON
76
- #
77
- # Example:
78
- # ENV['TEST_ENV'] = 'production'
79
- # load_config("config/config.yml")
80
- #
81
- # Will try to load the following files:
82
- # - config/config-production.yml
83
- # - config/config-debug.yml
84
- # - config/config-test.yml
85
- # - config/config-local.yml
86
- # - config/config.yml
87
- def load_config(config_name)
88
- # Split the filename
89
- ext = File.extname(config_name)
90
- dir, filename = File.split(config_name)
91
- basename = File.basename(filename, ext)
92
-
93
- # What are the suffixes to check
94
- suffixes = [
95
- "debug",
96
- "test",
97
- "local"
98
- ]
99
-
100
- if ENV["TEST_ENV"]
101
- @env = ENV["TEST_ENV"]
73
+ @metadata = Runtime.instance.set_if(self, :metadata) do
74
+ log.debug "Creating metadata storage"
75
+ Storage.new("metadata")
76
+ end
102
77
  end
103
78
 
104
- # Do we have an environment
105
- if not @env.nil?
106
- # Add it to the suffixes
107
- suffixes.unshift(@env)
79
+ def metadata
80
+ if @metadata.nil?
81
+ raise "No metadata available"
82
+ end
83
+ return @metadata
108
84
  end
109
85
 
110
- # Turn suffixes into files to try
111
- files = []
112
- suffixes.each do |suffix|
113
- files << "#{dir}#{File::SEPARATOR}#{basename}-#{suffix}#{ext}"
114
- end
115
- files << config_name
116
86
 
117
- # Try all files in order
118
- files.each do |file|
119
- # Check if files exist
120
- if File.file?(file)
121
- begin
122
- # Try to load a config file
123
- return self.load_config_from_file(file)
124
- rescue Exception => e
125
- raise e
87
+ ##
88
+ # Loads a config based on a filename
89
+ #
90
+ # Supports: YML, JSON
91
+ #
92
+ # Example:
93
+ # ENV['TEST_ENV'] = 'production'
94
+ # load_config("config/config.yml")
95
+ #
96
+ # Will try to load the following files:
97
+ # - config/config-production.yml
98
+ # - config/config-debug.yml
99
+ # - config/config-test.yml
100
+ # - config/config-local.yml
101
+ # - config/config.yml
102
+ #
103
+ # Throws errors if:
104
+ # - Config file isn't readable
105
+ # - Environment doesn't exist in config
106
+ # - Default environment not set in config if no environment is set
107
+ def load_config(config_names)
108
+ # Go trough each config_name
109
+ config_names.each do |config_name|
110
+ files = []
111
+ # Split the filename
112
+ ext = File.extname(config_name)
113
+ dir, filename = File.split(config_name)
114
+ basename = File.basename(filename, ext)
115
+
116
+ # What are the suffixes to check
117
+ suffixes = %w(debug test local)
118
+
119
+ if ENV["TEST_ENV"]
120
+ @env = ENV["TEST_ENV"]
121
+ end
122
+
123
+ # Do we have an environment
124
+ unless @env.nil?
125
+ # Add it to the suffixes
126
+ suffixes.unshift(@env)
127
+ end
128
+
129
+ # Turn suffixes into files to try
130
+ suffixes.each do |suffix|
131
+ files << "#{dir}#{File::SEPARATOR}#{basename}-#{suffix}#{ext}"
132
+ end
133
+ files << config_name
134
+ # Try all files in order
135
+ files.each do |file|
136
+ # Check if files exist
137
+ if File.file?(file)
138
+ begin
139
+ # Try to load a config file
140
+ self.add_config_from_file(file)
141
+ break
142
+ rescue Exception => e
143
+ raise e
144
+ end
145
+ end
146
+ end
147
+ end
148
+ # If we have an environment, the config should contain it
149
+ if not @env.nil? and not self.has_config?(@env)
150
+ raise "Environment `#{@env}` doesn't exist in any of the config files"
151
+ end
152
+
153
+ # If we don't have one then load the default
154
+ if @env.nil? and self.has_config?("default_env")
155
+ tmp = self.config("default_env")
156
+ if self.has_config?(tmp)
157
+ @env = tmp
158
+ ENV['TEST_ENV'] = tmp
159
+ else
160
+ raise "Default environment not present in any of the config files"
126
161
  end
127
162
  end
128
163
  end
129
- end
130
164
 
131
165
 
132
- ##
133
- # Loads a config file
134
- #
135
- # Supports: YML, JSON
136
- #
137
- # Throws errors if:
138
- # - Config file isn't readable
139
- # - Environment doesn't exist in config
140
- # - Default environment not set in config if no environment is set
141
- def load_config_from_file(filename)
142
- # Set the global @config variable
143
- @config = get_config_from_file(filename)
144
-
145
- # If we have an environment this config should have it
146
- if not @env.nil? and not self.has_config?(@env)
147
- raise "Environment `#{@env}` doesn't exist in config file"
166
+ ##
167
+ # Loads a config file
168
+ #
169
+ # Supports: YML, JSON
170
+ #
171
+ # Adds the possibility to merge multiple config files.
172
+ def add_config_from_file(filename)
173
+ @config = {} if @config.nil?
174
+ # Add the data to the global config
175
+ @config.deep_merge! get_config_from_file(filename)
148
176
  end
149
177
 
150
- # If we don't have one then load the default
151
- if @env.nil? and self.has_config?("default_env")
152
- tmp = self.config("default_env")
153
- if self.has_config?(tmp)
154
- @env = tmp
155
- else
156
- raise "Default environment not present in config file"
157
- end
158
- end
159
- end
160
-
161
- # Adds the possibility to merge multiple config files.
162
- def add_config_from_file(filename)
163
- # Add the data to the global config
164
- @config.merge! get_config_from_file(filename)
165
- end
166
-
167
- # returns the data that's loaded from a config file.
168
- # Supports YAML and JSON
169
- def get_config_from_file(filename)
170
- # Try to load the file from disk
171
- begin
172
- # Determine the extension
173
- ext = File.extname(filename)
174
- # Use the correct loader
175
- if ext == ".yml"
176
- data = YAML.load_file(filename)
177
- elsif ext == ".json"
178
- json = File.read(filename)
179
- data = JSON.parse(json)
178
+ # returns the data that's loaded from a config file.
179
+ # Supports YAML and JSON
180
+ def get_config_from_file(filename)
181
+ # Try to load the file from disk
182
+ begin
183
+ # Determine the extension
184
+ ext = File.extname(filename)
185
+ # Use the correct loader
186
+ if ext == ".yml"
187
+ data = YAML.load_file(filename)
188
+ elsif ext == ".json"
189
+ json = File.read(filename)
190
+ data = JSON.parse(json)
191
+ end
192
+ rescue Exception => e
193
+ raise "Error loading file: #{filename} #{e}"
180
194
  end
181
- rescue Exception => e
182
- raise "Error loading file: #{filename} #{e}"
183
- end
184
195
 
185
- # Fix up empty files
186
- if data.nil? or data == false
187
- warn "Could not load configuration from '#{Config.config_file}'; it might be empty or malformed."
188
- data = {}
196
+ # Fix up empty files
197
+ if data.nil? or data == false
198
+ warn "Could not load configuration from '#{Config.config_files}'; it might be empty or malformed."
199
+ data = {}
200
+ end
201
+ return data
189
202
  end
190
- return data
191
- end
192
203
 
193
- ##
194
- # Does the config have a variable?
195
- # Uses config and catches any errors it raises
196
- def has_config?(variable)
197
- # Make sure the configured configuration is loaded, if possible
198
- init
204
+ ##
205
+ # Does the config have a variable?
206
+ # Uses config and catches any errors it raises
207
+ def has_config?(variable)
208
+ # Make sure the configured configuration is loaded, if possible
209
+ init
199
210
 
200
211
  begin
201
212
  value = self.config(variable)
@@ -203,220 +214,223 @@ module WorldModule
203
214
  rescue RuntimeError => err
204
215
  return false
205
216
  end
206
- end
207
-
208
- ##
209
- # Get the configuration from the config,
210
- # uses a dot seperator for object traversing
211
- #
212
- # Example:
213
- # ll.config("test.google.url") => "www.google.com"
214
- #
215
- # Raises error if traversing the object is impossible
216
- def config(variable=false, default=(no_default_set=true;nil))
217
- # Make sure the configured configuration is loaded, if possible
218
- init
219
-
220
- # No variable given? Return the entire object.
221
- result = @config
222
- if not variable
223
- return result
224
217
  end
225
218
 
226
- # Environment variables for known options override the option.
227
- if CONFIG_OPTIONS.has_key? variable
228
- var = variable.upcase
229
- if ENV.has_key? var
230
- return ENV[var]
219
+ ##
220
+ # Get the configuration from the config,
221
+ # uses a dot seperator for object traversing
222
+ #
223
+ # Example:
224
+ # ll.config("test.google.url") => "www.google.com"
225
+ #
226
+ # Raises error if traversing the object is impossible
227
+ def config(variable=false, default=(no_default_set=true; nil))
228
+ # Make sure the configured configuration is loaded, if possible
229
+ init
230
+
231
+ # No variable given? Return the entire object.
232
+ result = @config
233
+ if not variable
234
+ return result
231
235
  end
232
- end
233
236
 
234
- # Otherwise try to find it in the configuration object
235
- variable.split(".").each do |part|
236
- if no_default_set == true && result.nil?
237
- raise "Unknown configuration variable '#{variable}' and no default given!"
237
+ # Environment variables for known options override the option.
238
+ if CONFIG_OPTIONS.has_key? variable
239
+ var = variable.upcase
240
+ if ENV.has_key? var
241
+ return ENV[var]
242
+ end
238
243
  end
239
- break if result.nil?
240
- begin
241
- result = result[part]
242
- rescue TypeError, NoMethodError => ex
243
- warn "Could not read configuration variable #{variable}: #{ex}"
244
- break
244
+
245
+ # Otherwise try to find it in the configuration object
246
+ variable.split(".").each do |part|
247
+ if no_default_set == true && result.nil?
248
+ raise "Unknown configuration variable '#{variable}' and no default given!"
249
+ end
250
+ break if result.nil?
251
+ begin
252
+ result = result[part]
253
+ rescue TypeError, NoMethodError => ex
254
+ warn "Could not read configuration variable #{variable}: #{ex}"
255
+ break
256
+ end
245
257
  end
246
- end
247
258
 
248
- if default.nil? and result.nil?
249
- if CONFIG_OPTIONS.has_key? variable
250
- return CONFIG_OPTIONS[variable][0]
251
- elsif no_default_set == true
252
- raise "Unknown configuration variable '#{variable}' and no default given!"
259
+ if default.nil? and result.nil?
260
+ if CONFIG_OPTIONS.has_key? variable
261
+ return CONFIG_OPTIONS[variable][0]
262
+ elsif no_default_set == true
263
+ raise "Unknown configuration variable '#{variable}' and no default given!"
264
+ end
265
+ else
266
+ return result || default
253
267
  end
254
- else
255
- return result || default
256
268
  end
257
- end
258
269
 
259
- ##
260
- # Does the environment have a certain config variable
261
- def has_env?(variable)
262
- # Make sure the configured configuration is loaded, if possible
263
- init
270
+ ##
271
+ # Does the environment have a certain config variable
272
+ def has_env?(variable)
273
+ # Make sure the configured configuration is loaded, if possible
274
+ init
264
275
 
265
- if @env.nil?
266
- return false
276
+ if @env.nil?
277
+ return false
278
+ end
279
+ return self.has_config?("#{@env}.#{variable}")
267
280
  end
268
- return self.has_config?("#{@env}.#{variable}")
269
- end
270
-
271
- ##
272
- # Returns current environment
273
- def current_env
274
- init
275
281
 
276
- return @env
277
- end
282
+ ##
283
+ # Returns current environment
284
+ def current_env
285
+ init
278
286
 
279
- ##
280
- # Get a environment variable from the config file
281
- # Alias for ll.config(ll.env + "." + variable)
282
- def env(variable=false, default=(no_default_set=true;nil))
283
- # Make sure the configured configuration is loaded, if possible
284
- init
285
-
286
- if not variable
287
- return self.config(@env)
287
+ return @env
288
288
  end
289
289
 
290
- # Environment variables for known options override environment specific
291
- # options, too
292
- env_var = var_from_env(variable, default)
293
- if env_var != default
294
- return env_var
295
- end
296
-
297
- result = self.config("#{@env}.#{variable}",default)
290
+ ##
291
+ # Get a environment variable from the config file
292
+ # Alias for ll.config(ll.env + "." + variable)
293
+ def env(variable=false, default=(no_default_set=true; nil))
294
+ # Make sure the configured configuration is loaded, if possible
295
+ init
298
296
 
299
- if no_default_set == true and result.nil?
300
- raise "Unknown environment variable '#{@env}.#{variable}' and no default given"
301
- end
297
+ if not variable
298
+ return self.config(@env)
299
+ end
302
300
 
303
- return result
301
+ # Environment variables for known options override environment specific
302
+ # options, too
303
+ env_var = var_from_env(variable, default)
304
+ if env_var != default
305
+ return env_var
306
+ end
304
307
 
305
- end
308
+ result = self.config("#{@env}.#{variable}", default)
306
309
 
307
- ##
308
- # Checks if a variabl exist in the env or config
309
- def has_env_or_config?(variable)
310
- # Make sure the configured configuration is loaded, if possible
311
- init
310
+ if no_default_set == true and result.nil?
311
+ raise "Unknown environment variable '#{@env}.#{variable}' and no default given"
312
+ end
312
313
 
313
- return self.has_env?(variable) || self.has_config?(variable)
314
- end
314
+ return result
315
315
 
316
- ##
317
- # Get a variable from the config
318
- # First checks the environment section, before it checks the global part
319
- def env_or_config(variable, default=nil)
320
- # Make sure the configured configuration is loaded, if possible
321
- init
322
-
323
- # Environment variables for known options override environment specific
324
- # options, too
325
- env_var = var_from_env(variable, default)
326
- if env_var != default
327
- return env_var
328
316
  end
329
317
 
330
- if self.has_env?(variable)
331
- return self.env(variable, default)
332
- elsif self.has_config?(variable)
333
- return self.config(variable, default)
334
- else
335
- return default
318
+ ##
319
+ # Checks if a variabl exist in the env or config
320
+ def has_env_or_config?(variable)
321
+ # Make sure the configured configuration is loaded, if possible
322
+ init
323
+
324
+ return self.has_env?(variable) || self.has_config?(variable)
336
325
  end
337
- end
338
326
 
339
- def var_from_env(var, default=nil)
340
- # Simple solution for single depth variables like "browser"
341
- if ENV.has_key? var
342
- return ENV[var]
327
+ ##
328
+ # Get a variable from the config
329
+ # First checks the environment section, before it checks the global part
330
+ def env_or_config(variable, default=(no_default_set=true; nil))
331
+ # Make sure the configured configuration is loaded, if possible
332
+ init
333
+
334
+ # Environment variables for known options override environment specific
335
+ # options, too
336
+ env_var = var_from_env(variable, default)
337
+ if env_var != default
338
+ return env_var
339
+ end
340
+
341
+ if self.has_env?(variable)
342
+ return self.env(variable)
343
+ elsif self.has_config?(variable)
344
+ return self.config(variable)
345
+ else
346
+ if no_default_set == true
347
+ raise "Unknown environment or configuration variable '(#{@env}.)#{variable}' and no default given"
348
+ end
349
+ return default
350
+ end
343
351
  end
344
352
 
345
- value = default
353
+ def var_from_env(var, default=nil)
354
+ # Simple solution for single depth variables like "browser"
355
+ if ENV.has_key? var
356
+ return ENV[var]
357
+ end
358
+
359
+ value = default
346
360
 
347
- # Variables like:
348
- # var_from_env("remote.url","http://test.test")
349
- if var.is_a? String and
361
+ # Variables like:
362
+ # var_from_env("remote.url","http://test.test")
363
+ if var.is_a? String and
350
364
  not default.is_a? Hash
351
365
 
352
- # Env variables cannot contain a . replace them by _
353
- key_wanted = var.gsub(".","__")
366
+ # Env variables cannot contain a . replace them by _
367
+ key_wanted = var.gsub(".", "__")
354
368
 
355
- # Do a case insensitive compare
356
- ENV.keys.each do |key|
357
- if key.casecmp(key_wanted) == 0
358
- value = ENV[key]
359
- break
369
+ # Do a case insensitive compare
370
+ ENV.keys.each do |key|
371
+ if key.casecmp(key_wanted) == 0
372
+ value = ENV[key]
373
+ break
374
+ end
360
375
  end
361
- end
362
376
 
363
- # Environment:
364
- # REMOTE__USER=test
365
- # REMOTE__PASS=test
366
- # REMOTE__PROXY__HTTP=http://test.com
367
- #
368
- # Call:
369
- # var_from_env("remote",{})
370
- #
371
- # Result:
372
- # {"USER" => "test",
373
- # "PASS" => "test",
374
- # "proxy" => {"HTTP" => "http://test.con"}}
375
- elsif default.is_a? Hash
376
- # Env variables cannot contain a . replace them by _
377
- key_wanted = var.gsub(".","__")
378
- # Use a regular expression starting with the wanted key
379
- rgx = Regexp.new("^#{key_wanted}","i")
380
-
381
- result = {}
382
- # For each key check if it matched the regexp
383
- ENV.keys.each do |key|
384
- if (key =~ rgx) == 0
385
- tmp = result
386
- # Remove start and split into parts
387
- parts = key.sub(rgx, "").split("__")
388
- # Remove empty start if needed
389
- if parts[0].to_s.empty?
390
- parts.shift
391
- end
377
+ # Environment:
378
+ # REMOTE__USER=test
379
+ # REMOTE__PASS=test
380
+ # REMOTE__PROXY__HTTP=http://test.com
381
+ #
382
+ # Call:
383
+ # var_from_env("remote",{})
384
+ #
385
+ # Result:
386
+ # {"USER" => "test",
387
+ # "PASS" => "test",
388
+ # "proxy" => {"HTTP" => "http://test.con"}}
389
+ elsif default.is_a? Hash
390
+ # Env variables cannot contain a . replace them by _
391
+ key_wanted = var.gsub(".", "__")
392
+ # Use a regular expression starting with the wanted key
393
+ rgx = Regexp.new("^#{key_wanted}", "i")
394
+
395
+ result = {}
396
+ # For each key check if it matched the regexp
397
+ ENV.keys.each do |key|
398
+ if (key =~ rgx) == 0
399
+ tmp = result
400
+ # Remove start and split into parts
401
+ parts = key.sub(rgx, "").split("__")
402
+ # Remove empty start if needed
403
+ if parts[0].to_s.empty?
404
+ parts.shift
405
+ end
392
406
 
393
- # For each part
394
- parts.each_with_index do |part, index|
395
- # Final part should store the value in the hash
396
- if index == parts.length - 1
397
- tmp[part] = ENV[key]
398
- else
399
- # Otherwise, downcase the partname
400
- part.downcase!
401
- # Set it to an object if needed
402
- if !tmp.has_key? part
403
- tmp[part] = {}
407
+ # For each part
408
+ parts.each_with_index do |part, index|
409
+ # Final part should store the value in the hash
410
+ if index == parts.length - 1
411
+ tmp[part] = ENV[key]
412
+ else
413
+ # Otherwise, downcase the partname
414
+ part.downcase!
415
+ # Set it to an object if needed
416
+ if !tmp.has_key? part
417
+ tmp[part] = {}
418
+ end
419
+ # Assign tmp to the new hash
420
+ tmp = tmp[part]
404
421
  end
405
- # Assign tmp to the new hash
406
- tmp = tmp[part]
407
422
  end
408
423
  end
409
424
  end
410
- end
411
425
 
412
- # If we have set keys in the result return it
413
- if result.keys.length > 0
414
- return result
426
+ # If we have set keys in the result return it
427
+ if result.keys.length > 0
428
+ return result
429
+ end
415
430
  end
416
- end
417
431
 
418
- return value
419
- end
420
- end # module Config
421
- end # module WorldModule
432
+ return value
433
+ end
434
+ end # module Config
435
+ end # module WorldModule
422
436
  end # module LapisLazuli