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