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,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