qonfig 0.25.0 → 0.26.0

Sign up to get free protection for your applications and to get access to all the features.
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,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Definition order' do
4
+ specify 'config definitions dependes on the order' do
5
+ class FirstConfig < Qonfig::DataSet
6
+ setting :default, 100_500
7
+ setting :default do
8
+ setting :options, defined: true
9
+ end
10
+ end
11
+
12
+ FirstConfig.new.settings.tap do |config|
13
+ # { default: { options: { defined: true } } } is the last
14
+ expect(config.default.options).to match(defined: true)
15
+ end
16
+
17
+ class SecondConfig < Qonfig::DataSet
18
+ setting :default do
19
+ setting :options, defined: true
20
+ end
21
+ setting :default, 100_500
22
+ end
23
+
24
+ SecondConfig.new.settings.tap do |config|
25
+ # { default: 100_500 } is the last
26
+ expect(config.default).to eq(100_500)
27
+ end
28
+
29
+ class FirstComposedConfig < Qonfig::DataSet
30
+ compose FirstConfig
31
+ compose SecondConfig
32
+ end
33
+
34
+ FirstComposedConfig.new.settings.tap do |config|
35
+ # setting from SecondConfig (SecondConfig is the last)
36
+ expect(config.default).to eq(100_500)
37
+ end
38
+
39
+ class SecondComposedConfig < Qonfig::DataSet
40
+ compose SecondConfig
41
+ compose FirstConfig
42
+ end
43
+
44
+ SecondComposedConfig.new.settings.tap do |config|
45
+ # setting from FirstConfig (FirstConfig is the last)
46
+ expect(config.default.options).to match(defined: true)
47
+ end
48
+
49
+ class FirstAllInConfig < Qonfig::DataSet
50
+ setting :default, 123
51
+ compose FirstConfig
52
+ end
53
+
54
+ FirstAllInConfig.new.settings.tap do |config|
55
+ # setting from FirstConfig (FirstConfig is the last)
56
+ expect(config.default.options).to match(defined: true)
57
+ end
58
+
59
+ class SecondAllInConfig < Qonfig::DataSet
60
+ compose FirstConfig
61
+ setting :default, 123
62
+ end
63
+
64
+ SecondAllInConfig.new.settings.tap do |config|
65
+ # own setting (own setting is the last)
66
+ expect(config.default).to eq(123)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/SingleArgumentDig
4
+ describe '#dig-functionality (Hash#dig-like behaviour)' do
5
+ specify '#dig functionality works as expected :)' do
6
+ class DiggingConfig < Qonfig::DataSet
7
+ setting :db do
8
+ setting :creds do
9
+ setting :user, 'D@iVeR'
10
+ setting :password, 'test123'
11
+ setting :data, test: false
12
+ end
13
+ end
14
+ end
15
+
16
+ config = DiggingConfig.new
17
+
18
+ # acces to a value
19
+ expect(config.dig(:db, :creds, :user)).to eq('D@iVeR')
20
+ expect(config.dig('db', :creds, 'password')).to eq('test123')
21
+ expect(config.dig('db', 'creds', 'data')).to match(test: false)
22
+
23
+ # access to the settings
24
+ expect(config.dig(:db, :creds)).to be_a(Qonfig::Settings)
25
+ expect(config.dig(:db)).to be_a(Qonfig::Settings)
26
+
27
+ # try to dig into the hash value (setting with a hash value)
28
+ expect { config.dig(:db, :creds, :user, :test) }.to raise_error(Qonfig::UnknownSettingError)
29
+
30
+ # rubocop:disable Lint/RedundantSplatExpansion
31
+ # dig with empty key lists
32
+ expect { config.dig(*[]) }.to raise_error(Qonfig::ArgumentError)
33
+ expect { config.dig }.to raise_error(Qonfig::ArgumentError)
34
+ # rubocop:enable Lint/RedundantSplatExpansion
35
+
36
+ # dig into unexistent option
37
+ expect do
38
+ config.dig(:db, :creds, :session)
39
+ end.to raise_error(Qonfig::UnknownSettingError)
40
+
41
+ # dig into unexistent option
42
+ expect do
43
+ config.dig(:a, :b, :c, :d)
44
+ end.to raise_error(Qonfig::UnknownSettingError)
45
+ end
46
+ end
47
+ # rubocop:enable Style/SingleArgumentDig
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/SingleArgumentDig
4
+ describe 'Dot-notation' do
5
+ let(:config) do
6
+ Qonfig::DataSet.build do
7
+ setting :kek do
8
+ setting :pek do
9
+ setting :cheburek, 'test'
10
+ end
11
+
12
+ setting 'foo.bar', 100_500
13
+
14
+ setting :frek do
15
+ setting :jek do
16
+ setting :bek, 123_456
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ specify '#key? / #option? / #setting?' do
24
+ expect(config.key?('kek.pek.cheburek')).to eq(true)
25
+ expect(config.key?('kek.pek')).to eq(true)
26
+ expect(config.key?('kek')).to eq(true)
27
+ expect(config.key?('kek.foo.bar')).to eq(true)
28
+ expect(config.key?('kek.cheburek.pek')).to eq(false)
29
+ expect(config.key?('kek.cheburek')).to eq(false)
30
+
31
+ expect(config.option?('kek.pek.cheburek')).to eq(true)
32
+ expect(config.option?('kek.pek')).to eq(true)
33
+ expect(config.option?('kek')).to eq(true)
34
+ expect(config.option?('kek.foo.bar')).to eq(true)
35
+ expect(config.option?('kek.cheburek.pek')).to eq(false)
36
+ expect(config.option?('kek.cheburek')).to eq(false)
37
+
38
+ expect(config.setting?('kek.pek.cheburek')).to eq(true)
39
+ expect(config.setting?('kek.pek')).to eq(true)
40
+ expect(config.setting?('kek')).to eq(true)
41
+ expect(config.setting?('kek.foo.bar')).to eq(true)
42
+ expect(config.setting?('kek.cheburek.pek')).to eq(false)
43
+ expect(config.setting?('kek.cheburek')).to eq(false)
44
+ end
45
+
46
+ specify '#dig' do
47
+ expect(config.dig('kek.pek.cheburek')).to eq('test')
48
+ expect(config.dig('kek.pek')).to be_a(Qonfig::Settings)
49
+ expect(config.dig('kek')).to be_a(Qonfig::Settings)
50
+ expect(config.dig('kek.foo.bar')).to eq(100_500)
51
+
52
+ expect { config.dig('kek.pek.ululek') }.to raise_error(Qonfig::UnknownSettingError)
53
+ expect { config.dig('kek.ululek') }.to raise_error(Qonfig::UnknownSettingError)
54
+ expect { config.dig('ululek') }.to raise_error(Qonfig::UnknownSettingError)
55
+ end
56
+
57
+ specify '#subset' do
58
+ expect(config.subset('kek', 'kek.frek')).to match(
59
+ 'kek' => {
60
+ 'frek' => { 'jek' => { 'bek' => 123456 } },
61
+ 'pek' => { 'cheburek'=>'test' },
62
+ 'foo.bar' => 100_500
63
+ },
64
+ 'frek' => {
65
+ 'jek' => { 'bek' => 123_456 }
66
+ }
67
+ )
68
+
69
+ expect do
70
+ config.subset('kek', 'kek.frek', 'kek.lel', 'kek.foo')
71
+ end.to raise_error(Qonfig::UnknownSettingError)
72
+ end
73
+
74
+ specify '#slice' do
75
+ expect(config.slice('kek.pek')).to match('pek' => { 'cheburek' => 'test' })
76
+ expect(config.slice('kek.frek')).to match('frek' => { 'jek' => { 'bek' => 123_456 } })
77
+
78
+ # TODO: fix this
79
+ # expect(config.slice('kek.foo.bar')).to match('foo.bar' => 100_500)
80
+
81
+ expect { config.slice('lel') }.to raise_error(Qonfig::UnknownSettingError)
82
+ expect { config.slice('kek.lek') }.to raise_error(Qonfig::UnknownSettingError)
83
+ end
84
+
85
+ specify '#slice_value' do
86
+ expect(config.slice_value('kek.pek.cheburek')).to eq('test')
87
+ expect(config.slice_value('kek.pek')).to match('cheburek' => 'test')
88
+ expect(config.slice_value('kek.frek')).to match('jek' => { 'bek' => 123_456 })
89
+ expect(config.slice_value('kek.foo.bar')).to eq(100_500)
90
+
91
+ expect { config.slice_value('kek.pek.lelek') }.to raise_error(Qonfig::UnknownSettingError)
92
+ expect { config.slice_value('kek.frek.bek') }.to raise_error(Qonfig::UnknownSettingError)
93
+ expect { config.slice_value('lel') }.to raise_error(Qonfig::UnknownSettingError)
94
+ expect { config.slice_value('kek.foo') }.to raise_error(Qonfig::UnknownSettingError)
95
+ end
96
+
97
+ specify '#[]' do
98
+ expect(config['kek.pek.cheburek']).to eq('test')
99
+ expect(config['kek.pek']).to be_a(Qonfig::Settings)
100
+ expect(config['kek.pek']['cheburek']).to eq('test')
101
+ expect(config['kek']['frek.jek']['bek']).to eq(123_456)
102
+ expect(config['kek']['foo.bar']).to eq(100_500)
103
+ expect(config['kek.foo.bar']).to eq(100_500)
104
+
105
+ expect { config['kek.foo'] }.to raise_error(Qonfig::UnknownSettingError)
106
+ expect { config['kek.pek.lelek'] }.to raise_error(Qonfig::UnknownSettingError)
107
+ expect { config['kek.frek']['bek'] }.to raise_error(Qonfig::UnknownSettingError)
108
+ expect { config['lel'] }.to raise_error(Qonfig::UnknownSettingError)
109
+ end
110
+
111
+ describe '#to_h' do
112
+ specify 'default behavior' do
113
+ expect(config.to_h(dot_style: true)).to match(
114
+ 'kek.pek.cheburek' => 'test',
115
+ 'kek.foo.bar' => 100_500,
116
+ 'kek.frek.jek.bek' => 123_456
117
+ )
118
+ end
119
+
120
+ specify 'with key and value transformations' do
121
+ transformer = -> (value) { "#{value}!!" }
122
+
123
+ # transformations: key ONLY
124
+ hash = config.to_h(
125
+ dot_style: true,
126
+ key_transformer: transformer
127
+ )
128
+ expect(hash).to match(
129
+ 'kek.pek.cheburek!!' => 'test',
130
+ 'kek.foo.bar!!' => 100_500,
131
+ 'kek.frek.jek.bek!!' => 123_456
132
+ )
133
+
134
+ # transformations: value ONLY
135
+ hash = config.to_h(
136
+ dot_style: true,
137
+ value_transformer: transformer
138
+ )
139
+ expect(hash).to match(
140
+ 'kek.pek.cheburek' => 'test!!',
141
+ 'kek.foo.bar' => '100500!!',
142
+ 'kek.frek.jek.bek' => '123456!!'
143
+ )
144
+
145
+ # transformations: key AND value
146
+ hash = config.to_h(
147
+ dot_style: true,
148
+ key_transformer: transformer,
149
+ value_transformer: transformer
150
+ )
151
+ expect(hash).to match(
152
+ 'kek.pek.cheburek!!' => 'test!!',
153
+ 'kek.foo.bar!!' => '100500!!',
154
+ 'kek.frek.jek.bek!!' => '123456!!'
155
+ )
156
+ end
157
+ end
158
+ end
159
+ # rubocop:enable Style/SingleArgumentDig
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Export settings as instance-level access methods' do
4
+ let(:config) do
5
+ Qonfig::DataSet.build do
6
+ setting :credentials do
7
+ setting :login, '0exp'
8
+ setting :password, 'test123'
9
+ end
10
+
11
+ setting :queue do
12
+ setting :adapter, :sidekiq
13
+ setting :threads, 10
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:empty_config) { Qonfig::DataSet.build }
19
+
20
+ specify 'empty export does nothing' do
21
+ my_simple_object = Object.new
22
+
23
+ expect { empty_config.export_settings(my_simple_object, '*') }.not_to raise_error
24
+ expect { empty_config.export_settings(my_simple_object, '#') }.not_to raise_error
25
+ end
26
+
27
+ specify 'default values => do nothing' do
28
+ my_simple_object = Object.new
29
+ config.export_settings(my_simple_object) # do nothing :D
30
+
31
+ expect(my_simple_object).not_to respond_to(:credentials)
32
+ expect(my_simple_object).not_to respond_to(:login)
33
+ expect(my_simple_object).not_to respond_to(:queue)
34
+ expect(my_simple_object).not_to respond_to(:adaper)
35
+ expect(my_simple_object).not_to respond_to(:threads)
36
+ end
37
+
38
+ specify '<non-raw export> (concrete keys as values and keys with nestings as a hash)' do
39
+ my_simple_object = Object.new
40
+
41
+ config.export_settings(
42
+ my_simple_object,
43
+ 'credentials',
44
+ mappings: { adapter: 'queue.adapter' },
45
+ prefix: 'config_'
46
+ )
47
+
48
+ expect(my_simple_object).to respond_to(:config_credentials) # NOTE: hash
49
+ expect(my_simple_object).to respond_to(:config_adapter) # NOTE: value
50
+ expect(my_simple_object.config_credentials).to be_a(Hash)
51
+
52
+ expect(my_simple_object.config_credentials).to match('login' => '0exp', 'password' => 'test123')
53
+ expect(my_simple_object.config_adapter).to eq(:sidekiq)
54
+ end
55
+
56
+ specify 'support for predicates' do
57
+ my_simple_object = Object.new
58
+
59
+ # NOTE: without prefix
60
+ config.export_settings(my_simple_object, 'credentials.*', accessor: true)
61
+
62
+ # NOTE: with prefix
63
+ config.export_settings(my_simple_object, 'queue.*', prefix: 'config_', accessor: true)
64
+
65
+ # NOTE: and mappings
66
+ config.export_settings(
67
+ my_simple_object,
68
+ mappings: { creds_pass: 'credentials.password' },
69
+ accessor: true
70
+ )
71
+
72
+ expect(my_simple_object.login?).to eq(true)
73
+ expect(my_simple_object.password?).to eq(true)
74
+ expect(my_simple_object.config_adapter?).to eq(true)
75
+ expect(my_simple_object.config_threads?).to eq(true)
76
+ expect(my_simple_object.creds_pass?).to eq(true)
77
+
78
+ my_simple_object.config_adapter = nil
79
+ my_simple_object.password = false
80
+
81
+ expect(my_simple_object.config_adapter?).to eq(false)
82
+ expect(my_simple_object.password?).to eq(false)
83
+ expect(my_simple_object.creds_pass?).to eq(false)
84
+
85
+ my_simple_object.config_adapter = :delayed_job
86
+ my_simple_object.creds_pass = 'test'
87
+
88
+ expect(my_simple_object.config_adapter?).to eq(true)
89
+ expect(my_simple_object.password?).to eq(true)
90
+ expect(my_simple_object.creds_pass?).to eq(true)
91
+ end
92
+
93
+ specify '<raw export> (concrete keys as values and keys with nestings as Qonfig::Settings)' do
94
+ my_simple_object = Object.new
95
+
96
+ config.export_settings(
97
+ my_simple_object,
98
+ 'credentials',
99
+ mappings: { adapter: 'queue.adapter' },
100
+ prefix: 'kek_',
101
+ raw: true
102
+ )
103
+
104
+ expect(my_simple_object).to respond_to(:kek_credentials) # NOTE: Qonfig::Settings
105
+ expect(my_simple_object).to respond_to(:kek_adapter) # NOTE: value
106
+ expect(my_simple_object.kek_credentials).to be_a(Qonfig::Settings)
107
+
108
+ expect(my_simple_object.kek_credentials.login).to eq('0exp')
109
+ expect(my_simple_object.kek_credentials.password).to eq('test123')
110
+ expect(my_simple_object.kek_adapter).to eq(:sidekiq)
111
+ end
112
+
113
+ specify 'attr_writers (config muatators)' do
114
+ my_simple_object = Object.new
115
+
116
+ config.export_settings(
117
+ my_simple_object,
118
+ 'credentials.login', 'credentials',
119
+ mappings: { driver: 'queue.adapter' },
120
+ accessor: true
121
+ )
122
+
123
+ # NOTE: you can mutate config settings via exported attr_writers
124
+ my_simple_object.login = 'D@iVeR'
125
+ my_simple_object.driver = :delayed_job
126
+
127
+ # NOTE: check taht original config was changed
128
+ expect(config.settings.credentials.login).to eq('D@iVeR')
129
+ expect(config.settings.queue.adapter).to eq(:delayed_job)
130
+
131
+ # NOTE: check that reder returns new value
132
+ expect(my_simple_object.login).to eq('D@iVeR')
133
+ expect(my_simple_object.driver).to eq(:delayed_job)
134
+
135
+ # NOTE: some mutators can be ambiguous - be careful :thinking:
136
+ expect { my_simple_object.credentials = 123 }.to raise_error(Qonfig::AmbiguousSettingValueError)
137
+ end
138
+ end
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Expose JSON file' do
4
+ specify 'defines config object by json instructions and specific environment settings' do
5
+ class ExposeJSONConfig < Qonfig::DataSet
6
+ json_file_path = SpecSupport.fixture_path('expose_json', 'project.json')
7
+
8
+ setting :file_name_based do
9
+ setting :test_env do
10
+ # spec/fixtures/expose_json/project.test.json
11
+ expose_json json_file_path, via: :file_name, env: :test
12
+ end
13
+
14
+ setting :prod_env do
15
+ # spec/fixtures/expose_json/project.production.json
16
+ expose_json json_file_path, via: :file_name, env: :production
17
+ end
18
+
19
+ setting :stage_env do
20
+ # spec/fixtures/expose_json/project.staging.json
21
+ expose_json json_file_path, via: :file_name, env: :staging
22
+ end
23
+
24
+ setting :dev_env do
25
+ # spec/fixtures/expose_json/project.development.json
26
+ expose_json json_file_path, via: :file_name, env: :development
27
+ end
28
+ end
29
+
30
+ setting :env_based do
31
+ setting :test_env do
32
+ # spec/fixtures/expose_json/project.json
33
+ expose_json json_file_path, via: :env_key, env: :test
34
+ end
35
+
36
+ setting :prod_env do
37
+ # spec/fixtures/expose_json/project.json
38
+ expose_json json_file_path, via: :env_key, env: :production
39
+ end
40
+
41
+ setting :stage_env do
42
+ # spec/fixtures/expose_json/project.json
43
+ expose_json json_file_path, via: :env_key, env: :staging
44
+ end
45
+
46
+ setting :dev_env do
47
+ # spec/fixtures/expose_json/project.json
48
+ expose_json json_file_path, via: :env_key, env: :development
49
+ end
50
+ end
51
+ end
52
+
53
+ settings = ExposeJSONConfig.new.settings
54
+
55
+ # NOTE: file-name based expose
56
+ # spec/fixtures/expose_json/project.test.json
57
+ expect(settings.file_name_based.test_env.api_mode_enabled).to eq(false)
58
+ expect(settings.file_name_based.test_env.db_driver).to eq('in_memory')
59
+ expect(settings.file_name_based.test_env.logging).to eq(false)
60
+ expect(settings.file_name_based.test_env.credentials).to eq({})
61
+ # spec/fixtures/expose_json/project.production.json
62
+ expect(settings.file_name_based.prod_env.api_mode_enabled).to eq(true)
63
+ expect(settings.file_name_based.prod_env.db_driver).to eq('rom')
64
+ expect(settings.file_name_based.prod_env.logging).to eq(true)
65
+ expect(settings.file_name_based.prod_env.credentials).to eq({})
66
+ # spec/fixtures/expose_json/project.development.json
67
+ expect(settings.file_name_based.dev_env.api_mode_enabled).to eq(true)
68
+ expect(settings.file_name_based.dev_env.db_driver).to eq('sequel')
69
+ expect(settings.file_name_based.dev_env.logging).to eq(false)
70
+ expect(settings.file_name_based.dev_env.credentials).to eq({})
71
+ # spec/fixtures/expose_json/project.staging.json
72
+ expect(settings.file_name_based.stage_env.api_mode_enabled).to eq(true)
73
+ expect(settings.file_name_based.stage_env.db_driver).to eq('active_record')
74
+ expect(settings.file_name_based.stage_env.logging).to eq(true)
75
+ expect(settings.file_name_based.stage_env.credentials).to eq({})
76
+
77
+ # NOTE: environment based expose
78
+ # spec/fixtures/expose_json/project.json (key: 'test')
79
+ expect(settings.env_based.test_env.api_mode_enabled).to eq(true)
80
+ expect(settings.env_based.test_env.db_driver).to eq('in_memory')
81
+ expect(settings.env_based.test_env.logging).to eq(false)
82
+ expect(settings.env_based.test_env.throttle_requests).to eq(false)
83
+ expect(settings.env_based.test_env.credentials).to eq({})
84
+ # spec/fixtures/expose_json/project.json (key: 'production')
85
+ expect(settings.env_based.prod_env.api_mode_enabled).to eq(true)
86
+ expect(settings.env_based.prod_env.db_driver).to eq('rom')
87
+ expect(settings.env_based.prod_env.logging).to eq(true)
88
+ expect(settings.env_based.prod_env.throttle_requests).to eq(true)
89
+ expect(settings.env_based.prod_env.credentials).to eq({})
90
+ # spec/fixtures/expose_json/project.json (key: 'development')
91
+ expect(settings.env_based.dev_env.api_mode_enabled).to eq(true)
92
+ expect(settings.env_based.dev_env.db_driver).to eq('sequel')
93
+ expect(settings.env_based.dev_env.logging).to eq(false)
94
+ expect(settings.env_based.dev_env.throttle_requests).to eq(false)
95
+ expect(settings.env_based.dev_env.credentials).to eq({})
96
+ # spec/fixtures/expose_json/project.json (key: 'staging')
97
+ expect(settings.env_based.stage_env.api_mode_enabled).to eq(true)
98
+ expect(settings.env_based.stage_env.db_driver).to eq('active_record')
99
+ expect(settings.env_based.stage_env.logging).to eq(true)
100
+ expect(settings.env_based.stage_env.throttle_requests).to eq(true)
101
+ expect(settings.env_based.stage_env.credentials).to eq({})
102
+ end
103
+
104
+ specify 'support for Pathname in file path' do
105
+ class PathnameExposeJSONCheck < Qonfig::DataSet
106
+ expose_json(
107
+ Pathname.new(SpecSupport.fixture_path('expose_json', 'project.json')),
108
+ via: :env_key, env: :development
109
+ )
110
+ end
111
+
112
+ config = PathnameExposeJSONCheck.new
113
+
114
+ expect(config.settings.api_mode_enabled).to eq(true)
115
+ expect(config.settings.logging).to eq(false)
116
+ expect(config.settings.db_driver).to eq('sequel')
117
+ expect(config.settings.throttle_requests).to eq(false)
118
+ expect(config.settings.credentials).to eq({})
119
+ end
120
+
121
+ describe 'failures and inconsistent situations' do
122
+ describe 'definition level errors' do
123
+ specify 'fails when :env attribute has non-string / non-symbol / non-numeric value' do
124
+ expect do
125
+ Class.new(Qonfig::DataSet) do
126
+ expose_json SpecSupport.fixture_path(
127
+ 'expose_json', 'project.json'
128
+ ), via: :env_key, env: Object.new
129
+ end
130
+ end.to raise_error(Qonfig::ArgumentError)
131
+ end
132
+
133
+ specify 'fails when :env is empty' do
134
+ expect do
135
+ Class.new(Qonfig::DataSet) do
136
+ expose_json SpecSupport.fixture_path(
137
+ 'expose_json', 'project.json'
138
+ ), via: :env_key, env: ''
139
+ end
140
+ end.to raise_error(Qonfig::ArgumentError)
141
+ end
142
+
143
+ specify 'fails when provided :via is not supported' do
144
+ expect do
145
+ Class.new(Qonfig::DataSet) do
146
+ expose_json SpecSupport.fixture_path(
147
+ 'expose_json', 'project.json'
148
+ ), via: :auto, env: :production
149
+ end
150
+ end.to raise_error(Qonfig::ArgumentError)
151
+ end
152
+ end
153
+
154
+ describe 'initialization level errors' do
155
+ specify 'fails when env-based settings is represented as a non-hash-like data' do
156
+ # NOTE:
157
+ # - file: spec/fixtures/expose_json/incompatible_structure.json
158
+ # - :staging environment key has incorrect value (scalar)
159
+ # - :test environment key has correct value (hash)
160
+
161
+ class IncompatibleEnvBasedJSONConfig < Qonfig::DataSet
162
+ expose_json SpecSupport.fixture_path(
163
+ 'expose_json', 'incompatible_structure.json'
164
+ ), via: :env_key, env: :staging
165
+ end
166
+
167
+ expect do
168
+ IncompatibleEnvBasedJSONConfig.new
169
+ end.to raise_error(Qonfig::IncompatibleJSONStructureError)
170
+
171
+ class CompatibleEnvBasedJSONConfig < Qonfig::DataSet
172
+ expose_json SpecSupport.fixture_path(
173
+ 'expose_json', 'incompatible_structure.json'
174
+ ), via: :env_key, env: :test
175
+ end
176
+
177
+ expect { CompatibleEnvBasedJSONConfig.new }.not_to raise_error
178
+ end
179
+
180
+ specify 'fails when json structure is represented as a non-hash-like data in the root' do
181
+ # NOTE:
182
+ # - file: spec/fixtures/expose_json/incompatible_root_structure.json
183
+ # - in the root: array
184
+ # - inside array: correct json object with "staging" and "test" environments"
185
+ # - expected behaviour: exception (because of the root is an array)
186
+
187
+ class IncompatibleEnvBasedRootStructureJSONConfig < Qonfig::DataSet
188
+ expose_json SpecSupport.fixture_path(
189
+ 'expose_json', 'incompatible_root_structure.json'
190
+ ), via: :env_key, env: :test
191
+ end
192
+
193
+ expect do
194
+ IncompatibleEnvBasedRootStructureJSONConfig.new
195
+ end.to raise_error(Qonfig::IncompatibleJSONStructureError)
196
+ end
197
+
198
+ describe 'strict mode' do
199
+ specify(
200
+ 'disabled (non-strict): ' \
201
+ 'file existence requirement + json-env-key existence requirement'
202
+ ) do
203
+ # NOTE: file does not exist + env key does not exist in json file
204
+ class NoFileNonStrictExposeJSONConfig < Qonfig::DataSet
205
+ setting :non_strict_by_file do
206
+ expose_json SpecSupport.fixture_path(
207
+ 'expose_json', 'nonexistent.json'
208
+ ), strict: false, via: :file_name, env: :development
209
+ end
210
+
211
+ setting :non_strict_by_env do
212
+ expose_json SpecSupport.fixture_path(
213
+ 'expose_json', 'nonexistent.json'
214
+ ), strict: false, via: :env_key, env: :development
215
+ end
216
+ end
217
+
218
+ expect(NoFileNonStrictExposeJSONConfig.new.to_h).to match(
219
+ 'non_strict_by_file' => {},
220
+ 'non_strict_by_env' => {}
221
+ )
222
+
223
+ # NOTE: file is exist + env key does not exist in json file
224
+ class NoEnvKeyNonStrictExposeJSONConfig < Qonfig::DataSet
225
+ setting :non_strict_by_file do
226
+ expose_json SpecSupport.fixture_path(
227
+ 'expose_json', 'project.json'
228
+ ), strict: false, via: :file_name, env: :nonexistent
229
+ end
230
+
231
+ setting :non_strict_by_env do
232
+ expose_json SpecSupport.fixture_path(
233
+ 'expose_json', 'project.json'
234
+ ), strict: false, via: :env_key, env: :nonexistent
235
+ end
236
+ end
237
+
238
+ expect(NoEnvKeyNonStrictExposeJSONConfig.new.to_h).to match(
239
+ 'non_strict_by_file' => {},
240
+ 'non_strict_by_env' => {}
241
+ )
242
+ end
243
+
244
+ specify(
245
+ 'enabled (strict, by default): ' \
246
+ 'file existence requirement + json-env-key existence requirement'
247
+ ) do
248
+ # NOTE: file does not exist
249
+ class StrictFileViaFileNameJSONConfig < Qonfig::DataSet
250
+ expose_json SpecSupport.fixture_path(
251
+ 'expose_json', 'nonexistent.json'
252
+ ), via: :file_name, env: :production
253
+ end
254
+
255
+ # NOTE: file does not exist
256
+ class StrictFileViaEnvKeyJSONConfig < Qonfig::DataSet
257
+ expose_json SpecSupport.fixture_path(
258
+ 'expose_json', 'nonexistent.json'
259
+ ), via: :env_key, env: :production
260
+ end
261
+
262
+ # NOTE: file does not exist
263
+ expect { StrictFileViaFileNameJSONConfig.new }.to raise_error(Qonfig::FileNotFoundError)
264
+ expect { StrictFileViaEnvKeyJSONConfig.new }.to raise_error(Qonfig::FileNotFoundError)
265
+
266
+ # NOTE:
267
+ # - file exists but env key does not exist
268
+ # - file: spec/fixtures/expose_json/project.json
269
+ class NonExistentEnvKeyJSONConfig < Qonfig::DataSet
270
+ expose_json SpecSupport.fixture_path(
271
+ 'expose_json', 'project.json'
272
+ ), via: :env_key, env: :nonexistent
273
+ end
274
+
275
+ # NOTE: env key does not exist
276
+ expect { NonExistentEnvKeyJSONConfig.new }.to raise_error(Qonfig::ExposeError)
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end