qonfig 0.25.0 → 0.26.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/.rubocop.yml +19 -1
  4. data/.travis.yml +7 -2
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile.lock +102 -0
  7. data/README.md +2 -0
  8. data/gemfiles/with_external_deps.gemfile.lock +112 -0
  9. data/gemfiles/without_external_deps.gemfile.lock +102 -0
  10. data/lib/qonfig.rb +1 -1
  11. data/lib/qonfig/commands/definition/load_from_env.rb +2 -0
  12. data/lib/qonfig/commands/definition/load_from_env/value_converter.rb +2 -0
  13. data/lib/qonfig/imports/direct_key.rb +8 -2
  14. data/lib/qonfig/imports/mappings.rb +8 -2
  15. data/lib/qonfig/loaders/basic.rb +2 -0
  16. data/lib/qonfig/loaders/json.rb +2 -1
  17. data/lib/qonfig/settings.rb +5 -1
  18. data/lib/qonfig/settings/key_matcher.rb +1 -1
  19. data/lib/qonfig/uploaders/base.rb +2 -0
  20. data/lib/qonfig/uploaders/yaml.rb +2 -0
  21. data/lib/qonfig/version.rb +1 -1
  22. data/qonfig.gemspec +4 -6
  23. data/sig/.keep +0 -0
  24. data/spec/features/clear_options_spec.rb +92 -0
  25. data/spec/features/compacted_config_spec.rb +308 -0
  26. data/spec/features/composition_spec.rb +207 -0
  27. data/spec/features/config_definition_and_representation_spec.rb +535 -0
  28. data/spec/features/definition_order_spec.rb +69 -0
  29. data/spec/features/dig_functionality_spec.rb +47 -0
  30. data/spec/features/dot_notation_spec.rb +159 -0
  31. data/spec/features/export_settings_spec.rb +138 -0
  32. data/spec/features/expose_json_spec.rb +281 -0
  33. data/spec/features/expose_self/format_option_dynamic_spec.rb +69 -0
  34. data/spec/features/expose_self/format_option_json_spec.rb +74 -0
  35. data/spec/features/expose_self/format_option_unsupported_spec.rb +27 -0
  36. data/spec/features/expose_self/format_option_yaml_spec.rb +77 -0
  37. data/spec/features/expose_self_spec.rb +97 -0
  38. data/spec/features/expose_yaml_spec.rb +263 -0
  39. data/spec/features/freeze_state_spec.rb +122 -0
  40. data/spec/features/get_config_keys_spec.rb +62 -0
  41. data/spec/features/get_config_values_spec.rb +41 -0
  42. data/spec/features/has_a_key_spec.rb +48 -0
  43. data/spec/features/import_settings_spec.rb +323 -0
  44. data/spec/features/indifferent_access_spec.rb +57 -0
  45. data/spec/features/inheritance_spec.rb +110 -0
  46. data/spec/features/instantiation_without_class_definition_spec.rb +59 -0
  47. data/spec/features/iteration_over_setting_keys_spec.rb +48 -0
  48. data/spec/features/load_from_env_spec.rb +240 -0
  49. data/spec/features/load_from_json_spec.rb +97 -0
  50. data/spec/features/load_from_self/format_option_json_spec.rb +31 -0
  51. data/spec/features/load_from_self/format_option_unsupported_spec.rb +27 -0
  52. data/spec/features/load_from_self/format_option_yaml_spec.rb +49 -0
  53. data/spec/features/load_from_self/with_erb_instructions_spec.rb +33 -0
  54. data/spec/features/load_from_self/with_hash_like_data_representation_spec.rb +66 -0
  55. data/spec/features/load_from_self/with_non_hash_like_data_representation_spec.rb +19 -0
  56. data/spec/features/load_from_self/without_end_data_spec.rb +11 -0
  57. data/spec/features/load_from_yaml_spec.rb +110 -0
  58. data/spec/features/load_setting_values_from_file/by_instance_method_examples.rb +171 -0
  59. data/spec/features/load_setting_values_from_file/by_macros_examples.rb +165 -0
  60. data/spec/features/load_setting_values_from_file/load_from_json_spec.rb +21 -0
  61. data/spec/features/load_setting_values_from_file/load_from_self/json_format/end_data_with_env_spec.rb +100 -0
  62. data/spec/features/load_setting_values_from_file/load_from_self/json_format/with_end_data_spec.rb +129 -0
  63. data/spec/features/load_setting_values_from_file/load_from_self/json_format/with_incorrect_end_data_spec.rb +34 -0
  64. data/spec/features/load_setting_values_from_file/load_from_self/json_format/without_end_data_spec.rb +65 -0
  65. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/end_data_with_env_spec.rb +94 -0
  66. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/with_end_data_spec.rb +126 -0
  67. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/with_incorrect_end_data_spec.rb +32 -0
  68. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/without_end_data_spec.rb +65 -0
  69. data/spec/features/load_setting_values_from_file/load_from_yaml_spec.rb +21 -0
  70. data/spec/features/load_setting_values_from_file/shared_behavior_spec.rb +33 -0
  71. data/spec/features/mixin_spec.rb +387 -0
  72. data/spec/features/non_redefineable_core_methods_spec.rb +29 -0
  73. data/spec/features/plugins/pretty_print_spec.rb +86 -0
  74. data/spec/features/plugins/toml/expose_self/format_option_toml_spec.rb +71 -0
  75. data/spec/features/plugins/toml/expose_toml_spec.rb +221 -0
  76. data/spec/features/plugins/toml/load_from_self/format_option_toml_spec.rb +27 -0
  77. data/spec/features/plugins/toml/load_from_toml_spec.rb +109 -0
  78. data/spec/features/plugins/toml/load_setting_values_from_file/load_from_toml_spec.rb +27 -0
  79. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/end_data_with_env_spec.rb +95 -0
  80. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/with_end_data_spec.rb +125 -0
  81. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/with_incorrect_end_data_spec.rb +34 -0
  82. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/without_end_data_spec.rb +65 -0
  83. data/spec/features/plugins/toml/load_setting_values_from_file/shared_behavior_spec.rb +34 -0
  84. data/spec/features/plugins/toml/save_to_toml_spec.rb +149 -0
  85. data/spec/features/plugins/vault/expose_vault_spec.rb +117 -0
  86. data/spec/features/plugins/vault/load_from_vault_spec.rb +80 -0
  87. data/spec/features/plugins_spec.rb +89 -0
  88. data/spec/features/reload_spec.rb +75 -0
  89. data/spec/features/run_code_with_temporary_settings_spec.rb +104 -0
  90. data/spec/features/save_to_file/save_to_json_spec.rb +157 -0
  91. data/spec/features/save_to_file/save_to_yaml_spec.rb +189 -0
  92. data/spec/features/settings_as_predicates_spec.rb +47 -0
  93. data/spec/features/settings_redefinition_spec.rb +30 -0
  94. data/spec/features/slice_functionality_spec.rb +69 -0
  95. data/spec/features/subset_functionality_spec.rb +49 -0
  96. data/spec/features/validation_spec.rb +916 -0
  97. data/spec/fixtures/array_settings.yml +3 -0
  98. data/spec/fixtures/expose_json/incompatible_root_structure.json +6 -0
  99. data/spec/fixtures/expose_json/incompatible_structure.json +4 -0
  100. data/spec/fixtures/expose_json/project.development.json +7 -0
  101. data/spec/fixtures/expose_json/project.json +30 -0
  102. data/spec/fixtures/expose_json/project.production.json +7 -0
  103. data/spec/fixtures/expose_json/project.staging.json +7 -0
  104. data/spec/fixtures/expose_json/project.test.json +7 -0
  105. data/spec/fixtures/expose_yaml/incompatible_structure.yml +2 -0
  106. data/spec/fixtures/expose_yaml/project.development.yml +4 -0
  107. data/spec/fixtures/expose_yaml/project.production.yml +4 -0
  108. data/spec/fixtures/expose_yaml/project.staging.yml +4 -0
  109. data/spec/fixtures/expose_yaml/project.test.yml +4 -0
  110. data/spec/fixtures/expose_yaml/project.yml +25 -0
  111. data/spec/fixtures/json_array_sample.json +14 -0
  112. data/spec/fixtures/json_object_sample.json +9 -0
  113. data/spec/fixtures/json_with_empty_object.json +6 -0
  114. data/spec/fixtures/json_with_erb.json +6 -0
  115. data/spec/fixtures/plugins/toml/expose_toml/project.development.toml +4 -0
  116. data/spec/fixtures/plugins/toml/expose_toml/project.production.toml +4 -0
  117. data/spec/fixtures/plugins/toml/expose_toml/project.staging.toml +4 -0
  118. data/spec/fixtures/plugins/toml/expose_toml/project.test.toml +4 -0
  119. data/spec/fixtures/plugins/toml/expose_toml/project.toml +27 -0
  120. data/spec/fixtures/plugins/toml/mini_file.toml +6 -0
  121. data/spec/fixtures/plugins/toml/toml_sample_with_all_types.toml +72 -0
  122. data/spec/fixtures/plugins/toml/values_file/with_env.toml +13 -0
  123. data/spec/fixtures/plugins/toml/values_file/without_env.toml +6 -0
  124. data/spec/fixtures/rubocop_settings.yml +12 -0
  125. data/spec/fixtures/shared_settings_with_aliases.yml +10 -0
  126. data/spec/fixtures/travis_settings.yml +6 -0
  127. data/spec/fixtures/values_file/with_env.json +19 -0
  128. data/spec/fixtures/values_file/with_env.yml +17 -0
  129. data/spec/fixtures/values_file/without_env.json +8 -0
  130. data/spec/fixtures/values_file/without_env.yml +5 -0
  131. data/spec/fixtures/with_empty_hash.yml +4 -0
  132. data/spec/fixtures/with_erb_instructions.yml +3 -0
  133. data/spec/spec_helper.rb +27 -0
  134. data/spec/support/meta_scopes.rb +20 -0
  135. data/spec/support/spec_support.rb +42 -0
  136. metadata +128 -11
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Freeze state' do
4
+ context 'instance' do
5
+ specify 'freezing' do
6
+ class FrozenableConfig < Qonfig::DataSet
7
+ setting :api_mode_enabled, true
8
+
9
+ setting :api do
10
+ setting :format, :json
11
+ end
12
+ end
13
+
14
+ frozen_config = FrozenableConfig.new
15
+
16
+ # can modify config values
17
+ frozen_config.configure do |conf|
18
+ expect { conf.api_mode_enabled = nil }.not_to raise_error
19
+ expect { conf.api.format = :plain_text }.not_to raise_error
20
+ end
21
+
22
+ # can reload config object
23
+ class FrozenableConfig
24
+ setting :additionals, false
25
+ end
26
+
27
+ expect { frozen_config.clear! }.not_to raise_error
28
+ expect { frozen_config.reload! }.not_to raise_error
29
+
30
+ frozen_config.freeze!
31
+
32
+ # cannot modify config values
33
+ frozen_config.configure do |conf|
34
+ expect { conf.api_mode_enabled = false }.to raise_error(Qonfig::FrozenSettingsError)
35
+ expect { conf.api.format = :xml }.to raise_error(Qonfig::FrozenSettingsError)
36
+ expect { conf.additionals = true }.to raise_error(Qonfig::FrozenSettingsError)
37
+
38
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
39
+ expect { conf.api_mode_enabled = false }.to raise_error(::FrozenError)
40
+ expect { conf.api.format = :xml }.to raise_error(::FrozenError)
41
+ expect { conf.additionals = true }.to raise_error(::FrozenError)
42
+ else
43
+ expect { conf.api_mode_enabled = false }.to raise_error(::RuntimeError)
44
+ expect { conf.api.format = :xml }.to raise_error(::RuntimeError)
45
+ expect { conf.additionals = true }.to raise_error(::RuntimeError)
46
+ end
47
+ end
48
+
49
+ # cannot reload config object
50
+ class FrozenableConfig
51
+ setting :customizable, false
52
+ end
53
+
54
+ expect { frozen_config.reload! }.to raise_error(Qonfig::FrozenSettingsError)
55
+ expect { frozen_config.clear! }.to raise_error(Qonfig::FrozenSettingsError)
56
+
57
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
58
+ expect { frozen_config.reload! }.to raise_error(::FrozenError)
59
+ expect { frozen_config.clear! }.to raise_error(::FrozenError)
60
+ else
61
+ expect { frozen_config.reload! }.to raise_error(::RuntimeError)
62
+ expect { frozen_config.clear! }.to raise_error(::RuntimeError)
63
+ end
64
+
65
+ expect(frozen_config.to_h).to match(
66
+ 'api_mode_enabled' => true,
67
+ 'api' => {
68
+ 'format' => :json
69
+ },
70
+ 'additionals' => false
71
+ )
72
+ end
73
+ end
74
+
75
+ context 'definition' do
76
+ specify 'created instance should be frozen' do
77
+ config = Qonfig::DataSet.build do
78
+ setting :test, true
79
+ freeze_state!
80
+ end
81
+
82
+ expect(config.frozen?).to eq(true)
83
+ end
84
+
85
+ specify 'inherited classes should not be frozen' do
86
+ base_config_klass = Class.new(Qonfig::DataSet) do
87
+ setting :test, true
88
+ freeze_state!
89
+ end
90
+
91
+ children_config_klass = Class.new(base_config_klass)
92
+ children_config = children_config_klass.new
93
+
94
+ expect(children_config.frozen?).to eq(false)
95
+ end
96
+
97
+ specify 'composed configurations should not be frozen' do
98
+ class BaseWithFreezeConfig < Qonfig::DataSet
99
+ setting :test, true
100
+ freeze_state!
101
+ end
102
+
103
+ class CompositionOfFreezedConfigs < Qonfig::DataSet
104
+ setting :nested do
105
+ compose BaseWithFreezeConfig
106
+ end
107
+ compose BaseWithFreezeConfig
108
+ end
109
+
110
+ config = CompositionOfFreezedConfigs.new
111
+ expect(config.frozen?).to eq(false)
112
+
113
+ expect do
114
+ config.settings.test = false
115
+ config.settings.nested.test = false
116
+ end.not_to raise_error
117
+
118
+ expect(config.settings.test).to eq(false)
119
+ expect(config.settings.nested.test).to eq(false)
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Get config keys' do
4
+ let(:config) do
5
+ Qonfig::DataSet.build do
6
+ setting :credentials do
7
+ setting :social do
8
+ setting :service, 'instagram'
9
+ setting :login, '0exp'
10
+ end
11
+
12
+ setting :admin do
13
+ setting :enabled, true
14
+ end
15
+ end
16
+
17
+ setting :server do
18
+ setting :type, 'cloud'
19
+ setting :options do
20
+ setting :os, 'CentOS'
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ specify '(#keys) existing keys' do
27
+ expect(config.keys).to contain_exactly(
28
+ 'credentials.social.service',
29
+ 'credentials.social.login',
30
+ 'credentials.admin.enabled',
31
+ 'server.type',
32
+ 'server.options.os'
33
+ )
34
+ end
35
+
36
+ specify '(#keys) all key variants' do
37
+ expect(config.keys(all_variants: true)).to contain_exactly(
38
+ 'credentials',
39
+ 'credentials.social',
40
+ 'credentials.social.service',
41
+ 'credentials.social.login',
42
+ 'credentials.admin',
43
+ 'credentials.admin.enabled',
44
+ 'server',
45
+ 'server.type',
46
+ 'server.options',
47
+ 'server.options.os'
48
+ )
49
+ end
50
+
51
+ specify '(#root_keys) only root keys' do
52
+ expect(config.keys(only_root: true)).to contain_exactly(
53
+ 'credentials',
54
+ 'server'
55
+ )
56
+
57
+ expect(config.root_keys).to contain_exactly(
58
+ 'credentials',
59
+ 'server'
60
+ )
61
+ end
62
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Get config values' do
4
+ subject(:config) do
5
+ Qonfig::DataSet.build do
6
+ setting :some_nested_setting do
7
+ setting :username, 'your_cool_name_here'
8
+ setting :password, 'secure_password'
9
+ end
10
+
11
+ setting :role, 'cool guy'
12
+ end.settings
13
+ end
14
+
15
+ describe '#[]' do
16
+ it 'properly gets value by value' do
17
+ expect(config[:some_nested_setting][:username]).to eq('your_cool_name_here')
18
+ expect(config[:some_nested_setting][:password]).to eq('secure_password')
19
+ expect(config[:role]).to eq('cool guy')
20
+ end
21
+
22
+ context 'with key path' do
23
+ it 'properly gets value' do
24
+ expect(config[:some_nested_setting, :username]).to eq('your_cool_name_here')
25
+ expect(config[:some_nested_setting, :password]).to eq('secure_password')
26
+ end
27
+ end
28
+
29
+ context "when setting doesn't exist" do
30
+ it 'raises error' do
31
+ expect { config[:some_nested_setting, :kek] }.to raise_error(Qonfig::UnknownSettingError)
32
+ end
33
+ end
34
+
35
+ context 'when zero arguments passed' do
36
+ it 'raises error' do
37
+ expect { config[] }.to raise_error(Qonfig::ArgumentError)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Has a key? (option? / setting?)' do
4
+ specify 'setting key existence checker' do
5
+ config = Qonfig::DataSet.build do
6
+ setting :credentials do
7
+ setting :user, 'D@iVeR'
8
+ setting :password, 'test123'
9
+ end
10
+ end
11
+
12
+ # root key
13
+ expect(config.key?('credentials')).to eq(true)
14
+ expect(config.key?(:credentials)).to eq(true)
15
+
16
+ expect(config.option?('credentials')).to eq(true)
17
+ expect(config.option?(:credentials)).to eq(true)
18
+
19
+ expect(config.setting?('credentials')).to eq(true)
20
+ expect(config.setting?(:credentials)).to eq(true)
21
+
22
+ # deeply nested key (with mixed types of params)
23
+ expect(config.key?('credentials', :user)).to eq(true)
24
+ expect(config.key?(:credentials, 'user')).to eq(true)
25
+ expect(config.key?(:credentials, :user)).to eq(true)
26
+
27
+ expect(config.option?('credentials', :user)).to eq(true)
28
+ expect(config.option?(:credentials, 'user')).to eq(true)
29
+ expect(config.option?(:credentials, :user)).to eq(true)
30
+
31
+ expect(config.setting?('credentials', :user)).to eq(true)
32
+ expect(config.setting?(:credentials, 'user')).to eq(true)
33
+ expect(config.setting?(:credentials, :user)).to eq(true)
34
+
35
+ # nonexited key
36
+ expect(config.key?(:user, :password)).to eq(false)
37
+ expect(config.key?('options')).to eq(false)
38
+
39
+ expect(config.option?(:user, :password)).to eq(false)
40
+ expect(config.option?('options')).to eq(false)
41
+
42
+ expect(config.setting?(:user, :password)).to eq(false)
43
+ expect(config.setting?('options')).to eq(false)
44
+
45
+ # incorrect key type
46
+ expect { config.setting?(Object.new) }.to raise_error(Qonfig::ArgumentError)
47
+ end
48
+ end
@@ -0,0 +1,323 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Import settings as access methods to a class' do
4
+ let(:config) do
5
+ Qonfig::DataSet.build do
6
+ setting :credentials do
7
+ setting :admin, true
8
+ setting :login, 'D@iVeR'
9
+ setting :password, 'test123'
10
+ end
11
+
12
+ setting :job_que do
13
+ setting :adapter, :sidekiq
14
+ setting :options do
15
+ setting :server, 'cloud'
16
+ setting :auto_run, true
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ before { stub_const('AppConfig', config) }
23
+
24
+ specify 'empty import does nothing' do
25
+ expect do
26
+ Class.new do
27
+ include Qonfig::Imports
28
+ import_settings(AppConfig, '#')
29
+ end
30
+ end.not_to raise_error
31
+
32
+ expect do
33
+ Class.new do
34
+ include Qonfig::Imports
35
+ import_settings(AppConfig, '*')
36
+ end
37
+ end.not_to raise_error
38
+ end
39
+
40
+ specify 'each required setting is imported as an instance method with access to sliced value' do
41
+ class SimpleImportCheckApp
42
+ include Qonfig::Imports
43
+
44
+ import_settings(AppConfig, 'credentials.login', 'job_que.options')
45
+ end
46
+
47
+ app_with_configs = SimpleImportCheckApp.new
48
+
49
+ # NOTE: new methods
50
+ expect(app_with_configs).to respond_to(:login)
51
+ expect(app_with_configs).to respond_to(:options)
52
+
53
+ # NOTE: get config values
54
+ expect(app_with_configs.login).to eq('D@iVeR')
55
+ expect(app_with_configs.options).to match('server' => 'cloud', 'auto_run' => true)
56
+
57
+ # NOTE: change config options and try to get them from app_with_config again
58
+ AppConfig.configure do |config|
59
+ config.credentials.login = '0exp'
60
+ config.job_que.options.server = 'localhost'
61
+ config.job_que.options.auto_run = false
62
+ end
63
+
64
+ # NOTE: check cahnged configs
65
+ expect(app_with_configs.login).to eq('0exp')
66
+ expect(app_with_configs.options).to match('server' => 'localhost', 'auto_run' => false)
67
+
68
+ # NOTE: only att_readers are generated by default
69
+ expect { app_with_configs.login = 'test123' }.to raise_error(NoMethodError)
70
+ expect { app_with_configs.options = 'test123' }.to raise_error(NoMethodError)
71
+
72
+ # NOTE: check that original values isnt changed after NoMethodError exception
73
+ expect(app_with_configs.login).to eq('0exp')
74
+ expect(app_with_configs.options).to match('server' => 'localhost', 'auto_run' => false)
75
+ end
76
+
77
+ specify 'support for predicates' do
78
+ class ImportPredicatesCheck
79
+ include Qonfig::Imports
80
+
81
+ # NOTE: without prefix
82
+ import_settings(AppConfig, 'credentials.*', accessor: true)
83
+
84
+ # NOTE: with prefix
85
+ import_settings(AppConfig, 'job_que.options.*', prefix: 'config_', accessor: true)
86
+
87
+ # NOTE: and mappings
88
+ import_settings(AppConfig, mappings: { queue_engine: 'job_que.adapter' }, accessor: true)
89
+ end
90
+
91
+ app_with_predicates = ImportPredicatesCheck.new
92
+
93
+ expect(app_with_predicates.admin?).to eq(true)
94
+ expect(app_with_predicates.login?).to eq(true)
95
+ expect(app_with_predicates.password?).to eq(true)
96
+ expect(app_with_predicates.config_server?).to eq(true)
97
+ expect(app_with_predicates.config_auto_run?).to eq(true)
98
+ expect(app_with_predicates.queue_engine?).to eq(true)
99
+
100
+ app_with_predicates.config_server = nil
101
+ app_with_predicates.admin = false
102
+ app_with_predicates.queue_engine = nil
103
+
104
+ expect(app_with_predicates.config_server?).to eq(false)
105
+ expect(app_with_predicates.admin?).to eq(false)
106
+ expect(app_with_predicates.queue_engine?).to eq(false)
107
+
108
+ app_with_predicates.config_server = 'yandex'
109
+ app_with_predicates.admin = true
110
+ app_with_predicates.queue_engine = :delayed_job
111
+
112
+ expect(app_with_predicates.config_server?).to eq(true)
113
+ expect(app_with_predicates.admin?).to eq(true)
114
+ expect(app_with_predicates.queue_engine?).to eq(true)
115
+ end
116
+
117
+ specify 'attr_writers (config muatators)' do
118
+ class AttrAccessorImportCheckApp
119
+ include Qonfig::Imports
120
+
121
+ import_settings(
122
+ AppConfig,
123
+ 'credentials.login',
124
+ mappings: { job_opts: 'job_que.options' },
125
+ accessor: true
126
+ )
127
+ end
128
+
129
+ app_with_configs = AttrAccessorImportCheckApp.new
130
+
131
+ # NOTE: you can mutate config settings via imported attr_writers
132
+ app_with_configs.login = 'D@iVeR'
133
+
134
+ # NOTE: check that original config was changed
135
+ expect(AppConfig.settings.credentials.login).to eq('D@iVeR')
136
+
137
+ # NOTE: check that reader returns new value
138
+ expect(app_with_configs.login).to eq('D@iVeR')
139
+
140
+ # NOTE: some mutators can be ambiguous - be careful :thinking:
141
+ expect { app_with_configs.job_opts = 'test' }.to raise_error(Qonfig::AmbiguousSettingValueError)
142
+ end
143
+
144
+ specify 'you can prefix imported methods' do
145
+ class PrefixImportCheckApp
146
+ include Qonfig::Imports
147
+
148
+ import_settings(AppConfig, 'credentials.password', 'credentials.login', prefix: 'config_')
149
+ end
150
+
151
+ app_with_configs = PrefixImportCheckApp.new
152
+
153
+ expect(app_with_configs).to respond_to(:config_login)
154
+ expect(app_with_configs).to respond_to(:config_password)
155
+
156
+ expect(app_with_configs.config_login).to eq('D@iVeR')
157
+ expect(app_with_configs.config_password).to eq('test123')
158
+ end
159
+
160
+ specify 'you can define your own method name for each imported setting' do
161
+ class ImportWithMappingsApp
162
+ include Qonfig::Imports
163
+
164
+ # NOTE: without prefix
165
+ import_settings(AppConfig, mappings: {
166
+ user_password: 'credentials.password',
167
+ user_login: 'credentials.login',
168
+ job_adapter: 'job_que.adapter'
169
+ })
170
+
171
+ # NOTE: with prefix
172
+ import_settings(
173
+ AppConfig,
174
+ mappings: { job_server: 'job_que.options.server' },
175
+ prefix: 'config_'
176
+ )
177
+ end
178
+
179
+ app_with_configs = ImportWithMappingsApp.new
180
+
181
+ expect(app_with_configs).to respond_to(:user_password)
182
+ expect(app_with_configs).to respond_to(:user_login)
183
+ expect(app_with_configs).to respond_to(:job_adapter)
184
+ expect(app_with_configs).to respond_to(:config_job_server)
185
+
186
+ expect(app_with_configs.user_password).to eq('test123')
187
+ expect(app_with_configs.user_login).to eq('D@iVeR')
188
+ expect(app_with_configs.job_adapter).to eq(:sidekiq)
189
+ expect(app_with_configs.config_job_server).to eq('cloud')
190
+
191
+ # NOTE: change cofnigs and check that each new method returns the real config value
192
+ AppConfig.configure do |config|
193
+ config.credentials.password = 'tratata123'
194
+ config.credentials.login = '0exp'
195
+ config.job_que.adapter = :resque
196
+ config.job_que.options.server = 'aws'
197
+ end
198
+
199
+ expect(app_with_configs.user_password).to eq('tratata123')
200
+ expect(app_with_configs.user_login).to eq('0exp')
201
+ expect(app_with_configs.job_adapter).to eq(:resque)
202
+ expect(app_with_configs.config_job_server).to eq('aws')
203
+ end
204
+
205
+ specify 'you can mix explicit keys, mappings and prefix' do
206
+ class MixedImportCheckApp
207
+ include Qonfig::Imports
208
+
209
+ import_settings(
210
+ AppConfig,
211
+ 'credentials', 'job_que.options', # simple keys
212
+ mappings: { passwd: 'credentials.password', admn: 'credentials.admin' }, # mappings
213
+ prefix: 'config_' # and prefix
214
+ )
215
+ end
216
+
217
+ app_with_configs = MixedImportCheckApp.new
218
+
219
+ expect(app_with_configs).to respond_to(:config_passwd)
220
+ expect(app_with_configs).to respond_to(:config_admn)
221
+ expect(app_with_configs).to respond_to(:config_credentials)
222
+ expect(app_with_configs).to respond_to(:config_options)
223
+
224
+ expect(app_with_configs.config_passwd).to eq('test123')
225
+ expect(app_with_configs.config_admn).to eq(true)
226
+ expect(app_with_configs.config_credentials).to match(
227
+ 'admin' => true,
228
+ 'login' => 'D@iVeR',
229
+ 'password' => 'test123'
230
+ )
231
+ expect(app_with_configs.config_options).to eq(
232
+ 'server' => 'cloud',
233
+ 'auto_run' => true
234
+ )
235
+ end
236
+
237
+ specify 'raw setting import (Qonfig::Settings object - or <value>)' do
238
+ # NOTE: default non-raw import (import nested settings as hashes)
239
+ class NonRawImportCheckApp
240
+ include Qonfig::Imports
241
+ import_settings(AppConfig, 'job_que', mappings: { creds: 'credentials' }, raw: false)
242
+ end
243
+
244
+ app_with_configs = NonRawImportCheckApp.new
245
+
246
+ expect(app_with_configs).to respond_to(:job_que)
247
+ expect(app_with_configs).to respond_to(:creds)
248
+ expect(app_with_configs.creds).to be_a(Hash)
249
+ expect(app_with_configs.job_que).to be_a(Hash)
250
+ expect(app_with_configs.creds).to match(
251
+ 'admin' => true,
252
+ 'login' => 'D@iVeR',
253
+ 'password' => 'test123'
254
+ )
255
+ expect(app_with_configs.job_que).to match(
256
+ 'adapter' => :sidekiq,
257
+ 'options' => {
258
+ 'server' => 'cloud',
259
+ 'auto_run' => true
260
+ }
261
+ )
262
+
263
+ # NOTE: raw import (import nested settings as Qonfig::Settings objects)
264
+ class RawImportCheckApp
265
+ include Qonfig::Imports
266
+ import_settings(AppConfig, 'job_que', mappings: { creds: 'credentials' }, raw: true)
267
+ end
268
+
269
+ app_with_configs = RawImportCheckApp.new
270
+
271
+ expect(app_with_configs).to respond_to(:job_que)
272
+ expect(app_with_configs).to respond_to(:creds)
273
+ expect(app_with_configs.job_que).to be_a(Qonfig::Settings)
274
+ expect(app_with_configs.creds).to be_a(Qonfig::Settings)
275
+ expect(app_with_configs.job_que.adapter).to eq(:sidekiq)
276
+ expect(app_with_configs.job_que.options.server).to eq('cloud')
277
+ expect(app_with_configs.job_que.options.auto_run).to eq(true)
278
+ expect(app_with_configs.creds.admin).to eq(true)
279
+ expect(app_with_configs.creds.login).to eq('D@iVeR')
280
+ expect(app_with_configs.creds.password).to eq('test123')
281
+ end
282
+
283
+ specify 'incorrect attributes' do
284
+ expect do
285
+ Class.new do
286
+ include Qonfig::Imports
287
+ import_settings(123)
288
+ end
289
+ end.to raise_error(Qonfig::IncompatibleImportedConfigError)
290
+
291
+ expect do
292
+ Class.new do
293
+ include Qonfig::Imports
294
+ import_settings(AppConfig, 'kek.pek', prefix: Object.new)
295
+ end
296
+ end.to raise_error(Qonfig::IncorrectImportPrefixError)
297
+
298
+ expect do
299
+ Class.new do
300
+ include Qonfig::Imports
301
+ import_settings(AppConfig, 'pek.kek', 'kek.pek', Object.new)
302
+ end
303
+ end.to raise_error(Qonfig::IncorrectImportKeyError)
304
+
305
+ expect do
306
+ Class.new do
307
+ include Qonfig::Imports
308
+ import_settings(AppConfig, 'kek.pek', mappings: {
309
+ Object.new => 'kek.pek'
310
+ })
311
+ end
312
+ end.to raise_error(Qonfig::IncorrectImportMappingsError)
313
+
314
+ expect do
315
+ Class.new do
316
+ include Qonfig::Imports
317
+ import_settings(AppConfig, 'kek.pek', mappings: {
318
+ some_method: Object.new
319
+ })
320
+ end
321
+ end.to raise_error(Qonfig::IncorrectImportMappingsError)
322
+ end
323
+ end