lapis_lazuli 2.0.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +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