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,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Composition' do
4
+ specify 'config composition (multiple inheritance)' do
5
+ class WebAPIConfig < Qonfig::DataSet
6
+ setting :version, '0.1.0'
7
+ setting :header, 'app.vendor'
8
+ setting :strategy do
9
+ setting :format, :json
10
+ end
11
+ end
12
+
13
+ class ServerConfig < Qonfig::DataSet
14
+ setting :port, 8080
15
+ setting :host, '0.0.0.0'
16
+ setting :enable_middlewares, true
17
+ end
18
+
19
+ class DatabaseConfig < Qonfig::DataSet
20
+ setting :username, 'kek'
21
+ setting :password, 'pek'
22
+ setting :connection do
23
+ setting :address, 'google.com'
24
+ setting :port, 12_345
25
+ end
26
+ end
27
+
28
+ class ProjectConfig < Qonfig::DataSet
29
+ compose ServerConfig
30
+
31
+ setting :api do
32
+ compose WebAPIConfig
33
+ end
34
+
35
+ setting :db do
36
+ compose DatabaseConfig
37
+ end
38
+
39
+ setting :limits do
40
+ setting :withdraw, 1_000_000
41
+ setting :deposit, 3_000_000
42
+ end
43
+ end
44
+
45
+ config = ProjectConfig.new
46
+
47
+ config.settings.tap do |conf|
48
+ expect(conf.api.version).to eq('0.1.0')
49
+ expect(conf.api.header).to eq('app.vendor')
50
+ expect(conf.api.strategy.format).to eq(:json)
51
+ expect(conf.db.username).to eq('kek')
52
+ expect(conf.db.password).to eq('pek')
53
+ expect(conf.db.connection.address).to eq('google.com')
54
+ expect(conf.db.connection.port).to eq(12_345)
55
+ expect(conf.port).to eq(8080)
56
+ expect(conf.host).to eq('0.0.0.0')
57
+ expect(conf.enable_middlewares).to eq(true)
58
+ expect(conf.limits.withdraw).to eq(1_000_000)
59
+ expect(conf.limits.deposit).to eq(3_000_000)
60
+
61
+ expect(conf[:api][:version]).to eq('0.1.0')
62
+ expect(conf[:api][:header]).to eq('app.vendor')
63
+ expect(conf[:api][:strategy][:format]).to eq(:json)
64
+ expect(conf[:db][:username]).to eq('kek')
65
+ expect(conf[:db][:password]).to eq('pek')
66
+ expect(conf[:db][:connection][:address]).to eq('google.com')
67
+ expect(conf[:db][:connection][:port]).to eq(12_345)
68
+ expect(conf[:port]).to eq(8080)
69
+ expect(conf[:host]).to eq('0.0.0.0')
70
+ expect(conf[:enable_middlewares]).to eq(true)
71
+ expect(conf[:limits][:withdraw]).to eq(1_000_000)
72
+ expect(conf[:limits][:deposit]).to eq(3_000_000)
73
+ end
74
+
75
+ # hash representation
76
+ expect(config.to_h).to match(
77
+ 'db' => {
78
+ 'username' => 'kek',
79
+ 'password' => 'pek',
80
+ 'connection' => {
81
+ 'address' => 'google.com',
82
+ 'port' => 12_345
83
+ }
84
+ },
85
+ 'port' => 8080,
86
+ 'host' => '0.0.0.0',
87
+ 'enable_middlewares' => true,
88
+ 'limits' => {
89
+ 'withdraw' => 1_000_000,
90
+ 'deposit' => 3_000_000
91
+ },
92
+ 'api' => {
93
+ 'version' => '0.1.0',
94
+ 'header' => 'app.vendor',
95
+ 'strategy' => {
96
+ 'format' => :json
97
+ }
98
+ }
99
+ )
100
+
101
+ # reconfgure
102
+ config.configure do |conf|
103
+ conf.api.version = '0.2.0'
104
+ conf.api.header = 'app.super.vendor'
105
+ conf.api.strategy.format = :xml
106
+ conf.db.username = 'che'
107
+ conf.db.password = 'burek'
108
+ conf.db.connection.address = 'db.google.com'
109
+ conf.db.connection.port = 666
110
+ conf.port = 8081
111
+ conf.host = 'app.google.com'
112
+ conf.enable_middlewares = false
113
+ conf.limits.withdraw = 0
114
+ conf.limits.deposit = 1_000
115
+ end
116
+
117
+ config.settings.tap do |conf|
118
+ expect(conf.api.version).to eq('0.2.0')
119
+ expect(conf.api.header).to eq('app.super.vendor')
120
+ expect(conf.api.strategy.format).to eq(:xml)
121
+ expect(conf.db.username).to eq('che')
122
+ expect(conf.db.password).to eq('burek')
123
+ expect(conf.db.connection.address).to eq('db.google.com')
124
+ expect(conf.db.connection.port).to eq(666)
125
+ expect(conf.port).to eq(8081)
126
+ expect(conf.host).to eq('app.google.com')
127
+ expect(conf.enable_middlewares).to eq(false)
128
+ expect(conf.limits.withdraw).to eq(0)
129
+ expect(conf.limits.deposit).to eq(1_000)
130
+
131
+ expect(conf[:api][:version]).to eq('0.2.0')
132
+ expect(conf[:api][:header]).to eq('app.super.vendor')
133
+ expect(conf[:api][:strategy][:format]).to eq(:xml)
134
+ expect(conf[:db][:username]).to eq('che')
135
+ expect(conf[:db][:password]).to eq('burek')
136
+ expect(conf[:db][:connection][:address]).to eq('db.google.com')
137
+ expect(conf[:db][:connection][:port]).to eq(666)
138
+ expect(conf[:port]).to eq(8081)
139
+ expect(conf[:host]).to eq('app.google.com')
140
+ expect(conf[:enable_middlewares]).to eq(false)
141
+ expect(conf[:limits][:withdraw]).to eq(0)
142
+ expect(conf[:limits][:deposit]).to eq(1_000)
143
+ end
144
+
145
+ expect(config.to_h).to match(
146
+ 'db' => {
147
+ 'username' => 'che',
148
+ 'password' => 'burek',
149
+ 'connection' => {
150
+ 'address' => 'db.google.com',
151
+ 'port' => 666
152
+ }
153
+ },
154
+ 'port' => 8081,
155
+ 'host' => 'app.google.com',
156
+ 'enable_middlewares' => false,
157
+ 'limits' => {
158
+ 'withdraw' => 0,
159
+ 'deposit' => 1_000
160
+ },
161
+ 'api' => {
162
+ 'version' => '0.2.0',
163
+ 'header' => 'app.super.vendor',
164
+ 'strategy' => {
165
+ 'format' => :xml
166
+ }
167
+ }
168
+ )
169
+ end
170
+
171
+ specify 'composed config class should be a subtype of Qonfig::DataSet' do
172
+ InappropriateConfig = Class.new
173
+ AppropriateConfig = Class.new(Qonfig::DataSet)
174
+
175
+ expect do
176
+ # (with incompatible config class) on the root
177
+ Class.new(Qonfig::DataSet) do
178
+ compose InappropriateConfig
179
+ end
180
+ end.to raise_error(Qonfig::ArgumentError)
181
+
182
+ expect do
183
+ # (with compatible config class) on the root
184
+ Class.new(Qonfig::DataSet) do
185
+ compose AppropriateConfig
186
+ end
187
+ end.not_to raise_error
188
+
189
+ expect do
190
+ # (with incompatible config class) nested
191
+ Class.new(Qonfig::DataSet) do
192
+ setting :nested do
193
+ compose InappropriateConfig
194
+ end
195
+ end
196
+ end.to raise_error(Qonfig::ArgumentError)
197
+
198
+ expect do
199
+ # (with compatible config class) nested
200
+ Class.new(Qonfig::DataSet) do
201
+ setting :nested do
202
+ compose AppropriateConfig
203
+ end
204
+ end
205
+ end.not_to raise_error
206
+ end
207
+ end
@@ -0,0 +1,535 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Config definition and representation' do
4
+ specify 'config object definition, instantiation, settings access and mutation' do
5
+ class SimpleConfig < Qonfig::DataSet
6
+ # setting with nested options
7
+ setting :serializers do
8
+ # :native by default
9
+ setting :json, :native
10
+ end
11
+
12
+ # another setting with nested options
13
+ setting :mutations do
14
+ # subsetting
15
+ setting :action do
16
+ # nil by default
17
+ setting :query
18
+ end
19
+ end
20
+
21
+ # nested option reopening
22
+ setting :serializers do
23
+ # :native by default
24
+ setting :xml, :native
25
+ end
26
+
27
+ setting :defaults do
28
+ setting :test, false
29
+ end
30
+ # existing nested config redifinition (nested => value)
31
+ setting :defaults, nil
32
+
33
+ setting :shared, true
34
+ # existind setting redifinition (value => nested)
35
+ setting :shared do
36
+ setting :convert, false
37
+ end
38
+
39
+ # setting without nested options
40
+ setting :steps, 22
41
+ end
42
+
43
+ # instantiation
44
+ config = SimpleConfig.new
45
+
46
+ # access via method named as a setting key
47
+ expect(config.settings.serializers.json).to eq(:native)
48
+ expect(config.settings.serializers.xml).to eq(:native)
49
+ expect(config.settings.mutations.action.query).to eq(nil)
50
+ expect(config.settings.steps).to eq(22)
51
+ expect(config.settings.defaults).to eq(nil)
52
+ expect(config.settings.shared.convert).to eq(false)
53
+
54
+ # access via index named as a setting key
55
+ expect(config.settings[:serializers][:json]).to eq(:native)
56
+ expect(config.settings[:serializers][:xml]).to eq(:native)
57
+ expect(config.settings[:mutations][:action][:query]).to eq(nil)
58
+ expect(config.settings[:steps]).to eq(22)
59
+
60
+ # hash representation
61
+ expect(config.to_h).to match(
62
+ 'serializers' => {
63
+ 'json' => :native,
64
+ 'xml' => :native
65
+ },
66
+ 'defaults' => nil,
67
+ 'shared' => {
68
+ 'convert' => false
69
+ },
70
+ 'mutations' => {
71
+ 'action' => {
72
+ 'query' => nil
73
+ }
74
+ },
75
+ 'steps' => 22
76
+ )
77
+
78
+ # hash representation (with key transformation)
79
+ expect(config.to_h(key_transformer: proc { |value| value.to_sym })).to match(
80
+ serializers: {
81
+ json: :native,
82
+ xml: :native
83
+ },
84
+ defaults: nil,
85
+ shared: {
86
+ convert: false
87
+ },
88
+ mutations: {
89
+ action: {
90
+ query: nil
91
+ }
92
+ },
93
+ steps: 22
94
+ )
95
+
96
+ # hash representation (with value transformation)
97
+ expect(config.to_h(value_transformer: proc { |value| value.to_s })).to match(
98
+ 'serializers' => {
99
+ 'json' => 'native',
100
+ 'xml' => 'native'
101
+ },
102
+ 'defaults' => '',
103
+ 'shared' => {
104
+ 'convert' => 'false'
105
+ },
106
+ 'mutations' => {
107
+ 'action' => {
108
+ 'query' => ''
109
+ }
110
+ },
111
+ 'steps' => '22'
112
+ )
113
+
114
+ # hash representation (with key and value transformations)
115
+ expect(
116
+ config.to_h(
117
+ key_transformer: proc { |value| value.to_sym },
118
+ value_transformer: proc { |value| value.to_s }
119
+ )
120
+ ).to match(
121
+ serializers: {
122
+ json: 'native',
123
+ xml: 'native'
124
+ },
125
+ defaults: '',
126
+ shared: {
127
+ convert: 'false'
128
+ },
129
+ mutations: {
130
+ action: {
131
+ query: ''
132
+ }
133
+ },
134
+ steps: '22'
135
+ )
136
+
137
+ # configuration via block (classic style)
138
+ config.configure do |conf|
139
+ conf.serializers.json = :oj
140
+ conf.serializers.xml = :ox
141
+ conf.mutations.action.query = 'select'
142
+ conf.steps = 31
143
+ end
144
+
145
+ # access via method named as a setting key
146
+ expect(config.settings.serializers.json).to eq(:oj)
147
+ expect(config.settings.serializers.xml).to eq(:ox)
148
+ expect(config.settings.mutations.action.query).to eq('select')
149
+ expect(config.settings.steps).to eq(31)
150
+
151
+ # access via option index named as a setting key
152
+ expect(config.settings[:serializers][:json]).to eq(:oj)
153
+ expect(config.settings[:serializers][:xml]).to eq(:ox)
154
+ expect(config.settings[:mutations][:action][:query]).to eq('select')
155
+ expect(config.settings[:steps]).to eq(31)
156
+
157
+ # configuration via settings method
158
+ config.settings.serializers.json = 'circular'
159
+ config.settings.serializers.xml = 'angular'
160
+ config.settings.mutations.action.query = :update
161
+ config.settings.steps = nil
162
+
163
+ # access via method named as a setting key
164
+ expect(config.settings.serializers.json).to eq('circular')
165
+ expect(config.settings.serializers.xml).to eq('angular')
166
+ expect(config.settings.mutations.action.query).to eq(:update)
167
+ expect(config.settings.steps).to eq(nil)
168
+
169
+ # access via option index named as a setting key
170
+ expect(config.settings[:serializers][:json]).to eq('circular')
171
+ expect(config.settings[:serializers][:xml]).to eq('angular')
172
+ expect(config.settings[:mutations][:action][:query]).to eq(:update)
173
+ expect(config.settings[:steps]).to eq(nil)
174
+
175
+ # configuration via index accessing
176
+ config.settings[:serializers][:json] = 'pararam'
177
+ config.settings[:serializers][:xml] = 'tratata'
178
+ config.settings[:mutations][:action][:query] = :upsert
179
+ config.settings[:steps] = 1234
180
+
181
+ # access via method named as a setting key
182
+ expect(config.settings.serializers.json).to eq('pararam')
183
+ expect(config.settings.serializers.xml).to eq('tratata')
184
+ expect(config.settings.mutations.action.query).to eq(:upsert)
185
+ expect(config.settings.steps).to eq(1234)
186
+
187
+ # access via option index named as a setting key
188
+ expect(config.settings[:serializers][:json]).to eq('pararam')
189
+ expect(config.settings[:serializers][:xml]).to eq('tratata')
190
+ expect(config.settings[:mutations][:action][:query]).to eq(:upsert)
191
+ expect(config.settings[:steps]).to eq(1234)
192
+
193
+ # instant configuration via proc
194
+ config = SimpleConfig.new do |conf|
195
+ conf.serializers.json = :native
196
+ conf.serializers.xml = :native
197
+ conf.mutations.action.query = 'delete'
198
+ conf.steps = 0
199
+ end
200
+
201
+ # access via method named as a setting key
202
+ expect(config.settings.serializers.json).to eq(:native)
203
+ expect(config.settings.serializers.xml).to eq(:native)
204
+ expect(config.settings.mutations.action.query).to eq('delete')
205
+ expect(config.settings.steps).to eq(0)
206
+
207
+ # access via option index named as a setting key
208
+ expect(config.settings[:serializers][:json]).to eq(:native)
209
+ expect(config.settings[:serializers][:xml]).to eq(:native)
210
+ expect(config.settings[:mutations][:action][:query]).to eq('delete')
211
+ expect(config.settings[:steps]).to eq(0)
212
+
213
+ # attempt to get an access to the unexistent setting
214
+ expect { config.settings.deserialization }.to raise_error(Qonfig::UnknownSettingError)
215
+ expect { config.settings.mutations.global }.to raise_error(Qonfig::UnknownSettingError)
216
+ expect { config.settings[:deserialization] }.to raise_error(Qonfig::UnknownSettingError)
217
+ expect { config.settings.mutations[:global] }.to raise_error(Qonfig::UnknownSettingError)
218
+ expect { config.settings.mutations[:global] = 1 }.to raise_error(Qonfig::UnknownSettingError)
219
+
220
+ # access via []-method with indifferent access and dot-notation
221
+ expect(config[:serializers]['json']).to eq(:native)
222
+ expect(config['serializers'][:xml]).to eq(:native)
223
+ expect(config[:mutations][:action][:query]).to eq('delete')
224
+ expect(config['steps']).to eq(0)
225
+ expect(config['serializers.json']).to eq(:native)
226
+ expect(config['serializers.xml']).to eq(:native)
227
+ expect(config['mutations.action.query']).to eq('delete')
228
+
229
+ # configuration via []=-method with/and/without dot-notation
230
+ config['serializers.json'] = :non_native
231
+ config['serializers.xml'] = :non_native
232
+ expect(config['serializers.json']).to eq(:non_native)
233
+ expect(config['serializers.xml']).to eq(:non_native)
234
+
235
+ # mutation of non-existent keys via []=-method fails with error
236
+ expect { config['serializers.toml'] = :native }.to raise_error(Qonfig::UnknownSettingError)
237
+ expect { config['mutations.operation'] = :upgrade }.to raise_error(Qonfig::UnknownSettingError)
238
+
239
+ # ambigous mutation via []=-method fails with corresponding error
240
+ expect do
241
+ config['mutations.action'] = :select
242
+ end.to raise_error(Qonfig::AmbiguousSettingValueError)
243
+
244
+ # hash representation
245
+ expect(config.to_h).to match(
246
+ 'serializers' => {
247
+ 'json' => :non_native,
248
+ 'xml' => :non_native
249
+ },
250
+ 'defaults' => nil,
251
+ 'shared' => {
252
+ 'convert' => false
253
+ },
254
+ 'mutations' => {
255
+ 'action' => {
256
+ 'query' => 'delete'
257
+ }
258
+ },
259
+ 'steps' => 0
260
+ )
261
+ end
262
+
263
+ specify 'configuration via hash / hash + proc (instant and not)' do
264
+ class HashConfigurableConfig < Qonfig::DataSet
265
+ setting :a do
266
+ setting :b
267
+ setting :c
268
+ end
269
+
270
+ setting :d
271
+ setting :e
272
+ end
273
+
274
+ # configure by hash (via .new)
275
+ config = HashConfigurableConfig.new(
276
+ a: {
277
+ b: { g: 33 },
278
+ c: 2
279
+ },
280
+ d: 33,
281
+ e: { f: 49 }
282
+ )
283
+ expect(config.to_h).to match(
284
+ 'a' => {
285
+ 'b' => { g: 33 },
286
+ 'c' => 2
287
+ },
288
+ 'd' => 33,
289
+ 'e' => { f: 49 }
290
+ )
291
+
292
+ # configure by hash (via #configure)
293
+ config = HashConfigurableConfig.new
294
+ config.configure(
295
+ a: {
296
+ b: 'test',
297
+ c: 'no_test'
298
+ },
299
+ d: 100_500,
300
+ e: false
301
+ )
302
+ expect(config.to_h).to match(
303
+ 'a' => {
304
+ 'b' => 'test',
305
+ 'c' => 'no_test'
306
+ },
307
+ 'd' => 100_500,
308
+ 'e' => false
309
+ )
310
+
311
+ # mixed: configure by hash + proc (via .new)
312
+ config = HashConfigurableConfig.new(d: false, e: true) do |conf|
313
+ conf.a.b = 123
314
+ conf.a.c = 456
315
+ end
316
+ expect(config.to_h).to match(
317
+ 'a' => {
318
+ 'b' => 123,
319
+ 'c' => 456
320
+ },
321
+ 'd' => false,
322
+ 'e' => true
323
+ )
324
+
325
+ # mixed: configure by hash + proc (via #configure)
326
+ config = HashConfigurableConfig.new
327
+ config.configure(a: { b: { c: 49 }, c: 55 }) do |conf|
328
+ conf.d = 0.55
329
+ conf.e = false
330
+ end
331
+ expect(config.to_h).to match(
332
+ 'a' => {
333
+ 'b' => { c: 49 },
334
+ 'c' => 55
335
+ },
336
+ 'd' => 0.55,
337
+ 'e' => false
338
+ )
339
+
340
+ # proc has higher priority
341
+ config = HashConfigurableConfig.new(a: { b: 1, c: 2 }, d: 3, e: 4) do |conf|
342
+ conf.a.b = 5
343
+ conf.a.c = 6
344
+ conf.d = 7
345
+ conf.e = 8
346
+ end
347
+ expect(config.to_h).to match(
348
+ 'a' => {
349
+ 'b' => 5,
350
+ 'c' => 6
351
+ },
352
+ 'd' => 7,
353
+ 'e' => 8
354
+ )
355
+
356
+ expect do
357
+ # nonexistent nested key
358
+ HashConfigurableConfig.new(a: { e: 55 })
359
+ end.to raise_error(Qonfig::UnknownSettingError)
360
+ expect do
361
+ # nonexistend nested key + proc
362
+ HashConfigurableConfig.new(a: { e: 55 }) { |conf| conf.d = 'test' }
363
+ end.to raise_error(Qonfig::UnknownSettingError)
364
+
365
+ expect do
366
+ # nonexistent root key
367
+ HashConfigurableConfig.new(g: 'test')
368
+ end.to raise_error(Qonfig::UnknownSettingError)
369
+ expect do
370
+ # nonexistent root key + proc
371
+ HashConfigurableConfig.new(g: 'test') { |conf| conf.e = false }
372
+ end.to raise_error(Qonfig::UnknownSettingError)
373
+
374
+ expect do
375
+ # attempt to override nested settings
376
+ HashConfigurableConfig.new(a: 100)
377
+ end.to raise_error(Qonfig::AmbiguousSettingValueError)
378
+ expect do
379
+ # attempt to override nested settings (+ proc)
380
+ HashConfigurableConfig.new(a: 100) { |conf| conf.a.b = :none }
381
+ end.to raise_error(Qonfig::AmbiguousSettingValueError)
382
+
383
+ # attempt to use non-hash object
384
+ [1, 1.0, Object.new, true, false, Class.new, Module.new, (proc {}), (-> {})].each do |non_hash|
385
+ expect do
386
+ # without proc
387
+ HashConfigurableConfig.new(non_hash)
388
+ end.to raise_error(Qonfig::ArgumentError)
389
+
390
+ expect do
391
+ # with valid proc
392
+ HashConfigurableConfig.new(non_hash) { |conf| conf.d = 55 }
393
+ end.to raise_error(Qonfig::ArgumentError)
394
+ end
395
+ end
396
+
397
+ specify '(#to_h): key (and value) transformer should be a type of Proc' do
398
+ empty_config = Class.new(Qonfig::DataSet).new
399
+
400
+ expect do
401
+ empty_config.to_h(key_transformer: 123)
402
+ end.to raise_error(Qonfig::IncorrectKeyTransformerError)
403
+
404
+ expect do
405
+ empty_config.to_h(value_transformer: 123)
406
+ end.to raise_error(Qonfig::IncorrectValueTransformerError)
407
+
408
+ expect do
409
+ empty_config.to_h(key_transformer: proc {}, value_transformer: 123)
410
+ end.to raise_error(Qonfig::IncorrectValueTransformerError)
411
+
412
+ expect do
413
+ empty_config.to_h(key_transformer: 123, value_transformer: proc {})
414
+ end.to raise_error(Qonfig::IncorrectKeyTransformerError)
415
+
416
+ expect do
417
+ empty_config.to_h(key_transformer: 123, value_transformer: 123)
418
+ end.to raise_error(Qonfig::IncorrectHashTransformationError)
419
+
420
+ expect do
421
+ empty_config.to_h(key_transformer: proc {}, value_transformer: proc {})
422
+ end.not_to raise_error
423
+
424
+ expect do
425
+ empty_config.to_h(key_transformer: proc {})
426
+ end.not_to raise_error
427
+
428
+ expect do
429
+ empty_config.to_h(value_transformer: proc {})
430
+ end.not_to raise_error
431
+
432
+ expect do
433
+ empty_config.to_h
434
+ end.not_to raise_error
435
+ end
436
+
437
+ specify 'only string and symbol keys are supported' do
438
+ [1, 1.0, Object.new, true, false, Class.new, Module.new, (proc {}), (-> {})].each do |key|
439
+ expect do
440
+ Class.new(Qonfig::DataSet) { setting(key) }
441
+ end.to raise_error(Qonfig::ArgumentError)
442
+ end
443
+
444
+ expect do
445
+ Class.new(Qonfig::DataSet) do
446
+ setting :a
447
+ setting 'b'
448
+ end
449
+ end.not_to raise_error
450
+ end
451
+
452
+ specify 'causes an error when tries to assign a setting value to an option ' \
453
+ 'which already have another nested options' do
454
+ class WithNestedOptionsConfig < Qonfig::DataSet
455
+ setting :database do
456
+ setting :hostname, 'localhost'
457
+ end
458
+ end
459
+
460
+ config = WithNestedOptionsConfig.new
461
+
462
+ expect do
463
+ config.configure { |conf| conf.database = double }
464
+ end.to raise_error(Qonfig::AmbiguousSettingValueError)
465
+
466
+ expect do
467
+ config.configure { |conf| conf[:database] = double }
468
+ end.to raise_error(Qonfig::AmbiguousSettingValueError)
469
+
470
+ expect do
471
+ config.configure { |conf| conf[:database][:hostname] = double }
472
+ end.not_to raise_error
473
+
474
+ expect do
475
+ config.configure { |conf| conf.database.hostname = double }
476
+ end.not_to raise_error
477
+ end
478
+
479
+ specify 'fails when tries to use a non-string/non-symbol value as a setting key' do
480
+ incorrect_key_values = [123, Object.new, 15.1, (proc {}), Class.new, true, false]
481
+ correct_key_values = ['test', :test]
482
+
483
+ incorrect_key_values.each do |incorrect_key|
484
+ # check root
485
+ expect do
486
+ Class.new(Qonfig::DataSet) { setting incorrect_key }
487
+ end.to raise_error(Qonfig::ArgumentError)
488
+
489
+ # check nested
490
+ expect do
491
+ Class.new(Qonfig::DataSet) do
492
+ setting incorrect_key do
493
+ setting :any
494
+ end
495
+ end
496
+ end.to raise_error(Qonfig::ArgumentError)
497
+
498
+ # check nested
499
+ expect do
500
+ Class.new(Qonfig::DataSet) do
501
+ setting :any do
502
+ setting incorrect_key
503
+ end
504
+ end
505
+ end.to raise_error(Qonfig::ArgumentError)
506
+ end
507
+
508
+ correct_key_values.each do |correct_key|
509
+ # check root
510
+ expect do
511
+ Class.new(Qonfig::DataSet) do
512
+ setting correct_key
513
+ end
514
+ end.not_to raise_error
515
+
516
+ # check nested
517
+ expect do
518
+ Class.new(Qonfig::DataSet) do
519
+ setting correct_key do
520
+ setting :any
521
+ end
522
+ end
523
+ end.not_to raise_error
524
+
525
+ # check nested do
526
+ expect do
527
+ Class.new(Qonfig::DataSet) do
528
+ setting :any do
529
+ setting correct_key
530
+ end
531
+ end
532
+ end.not_to raise_error
533
+ end
534
+ end
535
+ end