pdk 1.13.0 → 1.14.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +25 -0
- data/README.md +41 -0
- data/lib/pdk.rb +0 -13
- data/lib/pdk/analytics.rb +18 -2
- data/lib/pdk/analytics/client/google_analytics.rb +3 -0
- data/lib/pdk/answer_file.rb +4 -1
- data/lib/pdk/cli.rb +7 -2
- data/lib/pdk/cli/build.rb +2 -2
- data/lib/pdk/cli/bundle.rb +7 -1
- data/lib/pdk/cli/console.rb +148 -0
- data/lib/pdk/cli/convert.rb +2 -2
- data/lib/pdk/cli/exec.rb +14 -14
- data/lib/pdk/cli/exec/command.rb +16 -11
- data/lib/pdk/cli/exec/interactive_command.rb +4 -0
- data/lib/pdk/cli/exec_group.rb +5 -5
- data/lib/pdk/cli/module/build.rb +0 -2
- data/lib/pdk/cli/module/generate.rb +1 -2
- data/lib/pdk/cli/new.rb +1 -1
- data/lib/pdk/cli/new/defined_type.rb +2 -0
- data/lib/pdk/cli/new/provider.rb +2 -0
- data/lib/pdk/cli/new/task.rb +2 -0
- data/lib/pdk/cli/new/{unit_test.rb → test.rb} +16 -12
- data/lib/pdk/cli/new/transport.rb +2 -0
- data/lib/pdk/cli/test/unit.rb +5 -3
- data/lib/pdk/cli/update.rb +2 -3
- data/lib/pdk/cli/util.rb +45 -14
- data/lib/pdk/cli/util/spinner.rb +2 -2
- data/lib/pdk/cli/validate.rb +6 -2
- data/lib/pdk/config.rb +20 -8
- data/lib/pdk/config/analytics_schema.json +26 -0
- data/lib/pdk/config/json.rb +14 -3
- data/lib/pdk/config/json_schema_namespace.rb +143 -0
- data/lib/pdk/config/json_schema_setting.rb +53 -0
- data/lib/pdk/config/json_with_schema.rb +50 -0
- data/lib/pdk/config/namespace.rb +84 -76
- data/lib/pdk/config/setting.rb +132 -0
- data/lib/pdk/config/yaml.rb +15 -3
- data/lib/pdk/config/yaml_with_schema.rb +59 -0
- data/lib/pdk/generate.rb +0 -2
- data/lib/pdk/generate/module.rb +29 -16
- data/lib/pdk/generate/puppet_object.rb +31 -28
- data/lib/pdk/module.rb +2 -2
- data/lib/pdk/module/build.rb +21 -8
- data/lib/pdk/module/convert.rb +64 -7
- data/lib/pdk/module/metadata.rb +5 -1
- data/lib/pdk/module/templatedir.rb +24 -7
- data/lib/pdk/module/update.rb +5 -1
- data/lib/pdk/module/update_manager.rb +21 -13
- data/lib/pdk/report.rb +4 -3
- data/lib/pdk/report/event.rb +5 -3
- data/lib/pdk/tests/unit.rb +36 -7
- data/lib/pdk/util.rb +20 -8
- data/lib/pdk/util/bundler.rb +14 -6
- data/lib/pdk/util/filesystem.rb +5 -0
- data/lib/pdk/util/git.rb +6 -0
- data/lib/pdk/util/puppet_strings.rb +24 -2
- data/lib/pdk/util/puppet_version.rb +25 -10
- data/lib/pdk/util/ruby_version.rb +13 -1
- data/lib/pdk/util/template_uri.rb +23 -2
- data/lib/pdk/util/vendored_file.rb +28 -24
- data/lib/pdk/util/version.rb +5 -5
- data/lib/pdk/validate/base_validator.rb +5 -4
- data/lib/pdk/validate/metadata/metadata_json_lint.rb +0 -4
- data/lib/pdk/validate/metadata/metadata_syntax.rb +5 -3
- data/lib/pdk/validate/metadata_validator.rb +0 -2
- data/lib/pdk/validate/puppet/puppet_epp.rb +4 -4
- data/lib/pdk/validate/puppet/puppet_lint.rb +0 -3
- data/lib/pdk/validate/puppet/puppet_syntax.rb +4 -4
- data/lib/pdk/validate/puppet_validator.rb +0 -2
- data/lib/pdk/validate/ruby/rubocop.rb +0 -5
- data/lib/pdk/validate/ruby_validator.rb +0 -2
- data/lib/pdk/validate/tasks/metadata_lint.rb +9 -5
- data/lib/pdk/validate/tasks/name.rb +4 -2
- data/lib/pdk/validate/tasks_validator.rb +0 -2
- data/lib/pdk/validate/yaml/syntax.rb +4 -4
- data/lib/pdk/validate/yaml_validator.rb +0 -2
- data/lib/pdk/version.rb +1 -1
- data/locales/pdk.pot +351 -311
- metadata +11 -7
- data/lib/pdk/config/validator.rb +0 -31
- data/lib/pdk/config/value.rb +0 -94
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"definitions": {},
|
3
|
+
"$schema": "http://json-schema.org/draft-06/schema#",
|
4
|
+
"$id": "http://puppet.com/schema/does_not_exist.json",
|
5
|
+
"type": "object",
|
6
|
+
"title": "The PDK Analytics YAML Schema",
|
7
|
+
"properties": {
|
8
|
+
"disabled": {
|
9
|
+
"$id": "#/properties/disabled",
|
10
|
+
"type": "boolean",
|
11
|
+
"title": "Disabled property",
|
12
|
+
"examples": [
|
13
|
+
false
|
14
|
+
]
|
15
|
+
},
|
16
|
+
"user-id": {
|
17
|
+
"$id": "#/properties/user-id",
|
18
|
+
"type": "string",
|
19
|
+
"title": "The User-id for analytics",
|
20
|
+
"examples": [
|
21
|
+
"cb9ed65f-37dc-48d8-9863-8bd09cbb61c7"
|
22
|
+
],
|
23
|
+
"pattern": "^[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}$"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
data/lib/pdk/config/json.rb
CHANGED
@@ -3,16 +3,27 @@ require 'pdk/config/namespace'
|
|
3
3
|
module PDK
|
4
4
|
class Config
|
5
5
|
class JSON < Namespace
|
6
|
-
|
7
|
-
|
6
|
+
# Parses a JSON document.
|
7
|
+
#
|
8
|
+
# @see PDK::Config::Namespace.parse_file
|
9
|
+
def parse_file(filename)
|
10
|
+
raise unless block_given?
|
11
|
+
data = load_data(filename)
|
12
|
+
return if data.nil? || data.empty?
|
8
13
|
|
9
14
|
require 'json'
|
10
15
|
|
11
|
-
::JSON.parse(data)
|
16
|
+
data = ::JSON.parse(data)
|
17
|
+
return if data.nil? || data.empty?
|
18
|
+
|
19
|
+
data.each { |k, v| yield k, PDK::Config::Setting.new(k, self, v) }
|
12
20
|
rescue ::JSON::ParserError => e
|
13
21
|
raise PDK::Config::LoadError, e.message
|
14
22
|
end
|
15
23
|
|
24
|
+
# Serializes object data into a JSON string.
|
25
|
+
#
|
26
|
+
# @see PDK::Config::Namespace.serialize_data
|
16
27
|
def serialize_data(data)
|
17
28
|
require 'json'
|
18
29
|
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'pdk/config/namespace'
|
2
|
+
|
3
|
+
# Due to https://github.com/ruby-json-schema/json-schema/issues/439
|
4
|
+
# Windows file paths "appear" as uri's with no host and a schema of drive letter
|
5
|
+
# Also it is not possible to craft a URI with a Windows path due to the URI object
|
6
|
+
# always prepending the path with forward slash (`/`) so Windows paths end up looking
|
7
|
+
# like '/C:\schema.json', which can not be read.
|
8
|
+
# Instead we need to monkey patch the Schema Reader reader to remove the errant forward slash
|
9
|
+
require 'json-schema/schema/reader'
|
10
|
+
module JSON
|
11
|
+
class Schema
|
12
|
+
class Reader
|
13
|
+
alias original_read_file read_file
|
14
|
+
|
15
|
+
def read_file(pathname)
|
16
|
+
new_pathname = JSON::Util::URI.unescaped_path(pathname.to_s)
|
17
|
+
# Munge the path if it looks like a Windows path e.g. /C:/Windows ...
|
18
|
+
# Note that UNC style paths do not have the same issue (\\host\path)
|
19
|
+
new_pathname.slice!(0) if new_pathname.start_with?('/') && new_pathname[2] == ':'
|
20
|
+
original_read_file(Pathname.new(new_pathname))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module PDK
|
27
|
+
class Config
|
28
|
+
class JSONSchemaNamespace < Namespace
|
29
|
+
# Initialises the PDK::Config::JSONSchemaNamespace object.
|
30
|
+
#
|
31
|
+
# @see PDK::Config::Namespace.initialize
|
32
|
+
#
|
33
|
+
# @option params [String] :schema_file Path to the JSON Schema document
|
34
|
+
def initialize(name = nil, file: nil, parent: nil, persistent_defaults: false, schema_file: nil, &block)
|
35
|
+
super(name, file: file, parent: parent, persistent_defaults: persistent_defaults, &block)
|
36
|
+
@schema_file = schema_file
|
37
|
+
@unmanaged_settings = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
# The JSON Schema for the namespace
|
41
|
+
#
|
42
|
+
# @return [Hash]
|
43
|
+
def schema
|
44
|
+
document_schema.schema
|
45
|
+
end
|
46
|
+
|
47
|
+
# Whether the schema is valid but empty.
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
def empty_schema?
|
51
|
+
document_schema.schema.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Name of all the top level properties for the schema
|
55
|
+
#
|
56
|
+
# @return [String[]]
|
57
|
+
def schema_property_names
|
58
|
+
return [] if schema['properties'].nil?
|
59
|
+
schema['properties'].keys
|
60
|
+
end
|
61
|
+
|
62
|
+
# Extends the to_h namespace method to include unmanaged settings
|
63
|
+
#
|
64
|
+
# @see PDK::Config::Namespace.to_h
|
65
|
+
def to_h
|
66
|
+
# This may seem counter-intuitive but we need to call super first as the settings
|
67
|
+
# may not have been loaded yet, which means @unmanaged_settings will be empty.
|
68
|
+
# We call super first to force any file loading and then merge the unmanaged settings
|
69
|
+
settings_hash = super
|
70
|
+
@unmanaged_settings = {} if @unmanaged_settings.nil?
|
71
|
+
@unmanaged_settings.merge(settings_hash)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Validates a document (Hash table) against the schema
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
def validate_document!(document)
|
78
|
+
::JSON::Validator.validate!(schema, document)
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
# @!attribute [w] unmanaged_settings
|
84
|
+
# Sets the list of unmanaged settings. For subclass use only
|
85
|
+
#
|
86
|
+
# @param unmanaged_settings [Hash<String, Object]] A hashtable of all unmanaged settings which will be persisted, but not visible
|
87
|
+
# @protected
|
88
|
+
attr_writer :unmanaged_settings
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Override the create_setting method to always fail. This is called
|
93
|
+
# to dyanmically add settings. However as we're using a schema, no
|
94
|
+
# new settings can be created
|
95
|
+
#
|
96
|
+
# @see PDK::Config::Namespace.create_missing_setting
|
97
|
+
#
|
98
|
+
# @private
|
99
|
+
def create_missing_setting(key, _initial_value = nil)
|
100
|
+
raise ArgumentError, _("Setting '#{key}' does not exist'")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Create a valid, but empty schema
|
104
|
+
#
|
105
|
+
# @return [JSON::Schema]
|
106
|
+
def create_empty_schema
|
107
|
+
require 'json-schema'
|
108
|
+
::JSON::Schema.new({}, 'http://json-schema.org/draft-06/schema#')
|
109
|
+
end
|
110
|
+
|
111
|
+
# Lazily retrieve the JSON schema from disk for this namespace
|
112
|
+
#
|
113
|
+
# @return [JSON::Schema]
|
114
|
+
def document_schema
|
115
|
+
return @document_schema unless @document_schema.nil?
|
116
|
+
|
117
|
+
# Create an empty schema by default.
|
118
|
+
@document_schema = create_empty_schema
|
119
|
+
|
120
|
+
require 'json-schema'
|
121
|
+
|
122
|
+
return @document_schema if @schema_file.nil?
|
123
|
+
unless PDK::Util::Filesystem.file?(@schema_file)
|
124
|
+
raise PDK::Config::LoadError, _('Unable to open %{file} for reading. File does not exist') % {
|
125
|
+
file: @schema_file,
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
# The schema should not query external URI references, except for the meta-schema. Local files are allowed
|
130
|
+
schema_reader = ::JSON::Schema::Reader.new(
|
131
|
+
accept_file: true,
|
132
|
+
accept_uri: proc { |uri| uri.host.nil? || ['json-schema.org'].include?(uri.host) },
|
133
|
+
)
|
134
|
+
@document_schema = schema_reader.read(Addressable::URI.convert_path(@schema_file))
|
135
|
+
rescue ::JSON::Schema::JsonParseError => e
|
136
|
+
raise PDK::Config::LoadError, _('Unable to open %{file} for reading. JSON Error: %{msg}') % {
|
137
|
+
file: @schema_file,
|
138
|
+
msg: e.message,
|
139
|
+
}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'pdk/config/json_schema_namespace'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
class Config
|
5
|
+
class JSONSchemaSetting < PDK::Config::Setting
|
6
|
+
# Initialises the PDK::Config::JSONSchemaSetting object.
|
7
|
+
#
|
8
|
+
# @see PDK::Config::Setting.initialize
|
9
|
+
def initialize(_name, namespace, _initial_value)
|
10
|
+
raise 'The JSONSchemaSetting object can only be created within the JSONSchemaNamespace' unless namespace.is_a?(PDK::Config::JSONSchemaNamespace)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
# Verifies that the new setting value is valid by calling the JSON schema validator on
|
15
|
+
# a hash which includes the new setting
|
16
|
+
#
|
17
|
+
# @see PDK::Config::Setting.validate!
|
18
|
+
def validate!(value)
|
19
|
+
# Get the existing namespace data
|
20
|
+
new_document = namespace.to_h
|
21
|
+
# ... set the new value
|
22
|
+
new_document[@name] = value
|
23
|
+
begin
|
24
|
+
# ... add validate it
|
25
|
+
namespace.validate_document!(new_document)
|
26
|
+
rescue ::JSON::Schema::ValidationError => e
|
27
|
+
raise ArgumentError, _('%{key} %{message}') % {
|
28
|
+
key: qualified_name,
|
29
|
+
message: e.message,
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Evaluate the default setting, firstly from the JSON schema and then
|
35
|
+
# from any other default evaluators in the settings chain.
|
36
|
+
#
|
37
|
+
# @see PDK::Config::Setting.default
|
38
|
+
#
|
39
|
+
# @return [Object, nil] the result of evaluating the block given to
|
40
|
+
# {#default_to}, or `nil` if the setting has no default.
|
41
|
+
def default
|
42
|
+
# Return the default from the schema document if it exists
|
43
|
+
if namespace.schema_property_names.include?(@name)
|
44
|
+
prop_schema = namespace.schema['properties'][@name]
|
45
|
+
return prop_schema['default'] unless prop_schema['default'].nil?
|
46
|
+
end
|
47
|
+
# ... otherwise call the settings chain default
|
48
|
+
# and if that doesn't exist, just return nil
|
49
|
+
@previous_setting.nil? ? nil : @previous_setting.default
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'pdk/config/json_schema_namespace'
|
2
|
+
require 'pdk/config/json_schema_setting'
|
3
|
+
|
4
|
+
module PDK
|
5
|
+
class Config
|
6
|
+
class JSONWithSchema < JSONSchemaNamespace
|
7
|
+
# Parses a JSON document with a schema.
|
8
|
+
#
|
9
|
+
# @see PDK::Config::Namespace.parse_file
|
10
|
+
def parse_file(filename)
|
11
|
+
raise unless block_given?
|
12
|
+
data = load_data(filename)
|
13
|
+
data = '{}' if data.nil? || data.empty?
|
14
|
+
require 'json'
|
15
|
+
|
16
|
+
@raw_data = ::JSON.parse(data)
|
17
|
+
@raw_data = {} if @raw_data.nil?
|
18
|
+
|
19
|
+
begin
|
20
|
+
# Ensure the parsed document is actually valid
|
21
|
+
validate_document!(@raw_data)
|
22
|
+
rescue ::JSON::Schema::ValidationError => e
|
23
|
+
raise PDK::Config::LoadError, _('The configuration file %{filename} is not valid: %{message}') % {
|
24
|
+
filename: filename,
|
25
|
+
message: e.message,
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
schema_property_names.each do |key|
|
30
|
+
yield key, PDK::Config::JSONSchemaSetting.new(key, self, @raw_data[key])
|
31
|
+
end
|
32
|
+
|
33
|
+
# Remove all of the "known" settings from the schema and
|
34
|
+
# we're left with the settings that we don't manage.
|
35
|
+
self.unmanaged_settings = @raw_data.reject { |k, _| schema_property_names.include?(k) }
|
36
|
+
rescue ::JSON::ParserError => e
|
37
|
+
raise PDK::Config::LoadError, e.message
|
38
|
+
end
|
39
|
+
|
40
|
+
# Serializes object data into a JSON string.
|
41
|
+
#
|
42
|
+
# @see PDK::Config::Namespace.serialize_data
|
43
|
+
def serialize_data(data)
|
44
|
+
require 'json'
|
45
|
+
|
46
|
+
::JSON.pretty_generate(data)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/pdk/config/namespace.rb
CHANGED
@@ -26,10 +26,12 @@ module PDK
|
|
26
26
|
# @param block [Proc] a block that is evaluated within the new instance.
|
27
27
|
def initialize(name = nil, file: nil, parent: nil, persistent_defaults: false, &block)
|
28
28
|
@file = File.expand_path(file) unless file.nil?
|
29
|
-
@
|
29
|
+
@settings = {}
|
30
30
|
@name = name.to_s
|
31
31
|
@parent = parent
|
32
32
|
@persistent_defaults = persistent_defaults
|
33
|
+
@mounts = {}
|
34
|
+
@loaded_from_file = false
|
33
35
|
|
34
36
|
instance_eval(&block) if block_given?
|
35
37
|
end
|
@@ -43,9 +45,9 @@ module PDK
|
|
43
45
|
# @param block [Proc] a block that is evaluated within the new [self].
|
44
46
|
#
|
45
47
|
# @return [nil]
|
46
|
-
def
|
47
|
-
@
|
48
|
-
@
|
48
|
+
def setting(key, &block)
|
49
|
+
@settings[key.to_s] ||= PDK::Config::Setting.new(key.to_s, self)
|
50
|
+
@settings[key.to_s].instance_eval(&block) if block_given?
|
49
51
|
end
|
50
52
|
|
51
53
|
# Mount a provided [self] (or subclass) into the namespace.
|
@@ -64,7 +66,7 @@ module PDK
|
|
64
66
|
obj.parent = self
|
65
67
|
obj.name = key.to_s
|
66
68
|
obj.instance_eval(&block) if block_given?
|
67
|
-
|
69
|
+
@mounts[key.to_s] = obj
|
68
70
|
end
|
69
71
|
|
70
72
|
# Create and mount a new child namespace.
|
@@ -88,11 +90,21 @@ module PDK
|
|
88
90
|
#
|
89
91
|
# @return [Object] the requested value.
|
90
92
|
def [](key)
|
91
|
-
|
93
|
+
# Check if it's a mount first...
|
94
|
+
return @mounts[key.to_s] unless @mounts[key.to_s].nil?
|
95
|
+
# Check if it's a setting, otherwise nil
|
96
|
+
return nil if settings[key.to_s].nil?
|
97
|
+
return settings[key.to_s].value unless settings[key.to_s].value.nil?
|
98
|
+
default_value = settings[key.to_s].default
|
99
|
+
return default_value if default_value.nil? || !@persistent_defaults
|
100
|
+
# Persist the default value
|
101
|
+
settings[key.to_s].value = default_value
|
102
|
+
save_data
|
103
|
+
default_value
|
92
104
|
end
|
93
105
|
|
94
106
|
# Get the value of the named key or the provided default value if not
|
95
|
-
# present.
|
107
|
+
# present. Note that this does not trigger persistent defaults
|
96
108
|
#
|
97
109
|
# This differs from {#[]} in an important way in that it allows you to
|
98
110
|
# return a default value, which is not possible using `[] || default` as
|
@@ -105,7 +117,12 @@ module PDK
|
|
105
117
|
#
|
106
118
|
# @return [Object] the requested value.
|
107
119
|
def fetch(key, default_value)
|
108
|
-
|
120
|
+
# Check if it's a mount first...
|
121
|
+
return @mounts[key.to_s] unless @mounts[key.to_s].nil?
|
122
|
+
# Check if it's a setting, otherwise default_value
|
123
|
+
return default_value if settings[key.to_s].nil?
|
124
|
+
# Check if has a value, otherwise default_value
|
125
|
+
settings[key.to_s].value.nil? ? default_value : settings[key.to_s].value
|
109
126
|
end
|
110
127
|
|
111
128
|
# After the value has been set in memory, the value will then be
|
@@ -116,6 +133,8 @@ module PDK
|
|
116
133
|
#
|
117
134
|
# @return [nil]
|
118
135
|
def []=(key, value)
|
136
|
+
# You can't set the value of a mount
|
137
|
+
raise ArgumentError, _('Namespace mounts can not be set a value') unless @mounts[key.to_s].nil?
|
119
138
|
set_volatile_value(key, value)
|
120
139
|
# Persist the change
|
121
140
|
save_data
|
@@ -131,14 +150,11 @@ module PDK
|
|
131
150
|
# @return [Hash{String => Object}] the values from the namespace that
|
132
151
|
# should be persisted to disk.
|
133
152
|
def to_h
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
140
|
-
new_hash.delete_if { |_, v| v.nil? }
|
141
|
-
end
|
153
|
+
new_hash = {}
|
154
|
+
settings.each_pair { |k, v| new_hash[k] = v.value }
|
155
|
+
@mounts.each_pair { |k, mount_point| new_hash[k] = mount_point.to_h if mount_point.include_in_parent? }
|
156
|
+
new_hash.delete_if { |_, v| v.nil? }
|
157
|
+
new_hash
|
142
158
|
end
|
143
159
|
|
144
160
|
# Resolves all filtered settings, including child namespaces, fully namespaced and filling in default values.
|
@@ -146,20 +162,16 @@ module PDK
|
|
146
162
|
# @param filter [String] Only resolve setting names which match the filter. See #be_resolved? for matching rules
|
147
163
|
# @return [Hash{String => Object}] All resolved settings for example {'user.module_defaults.author' => 'johndoe'}
|
148
164
|
def resolve(filter = nil)
|
149
|
-
# Explicitly force values to be loaded if they have not already
|
150
|
-
# done so. This will not cause them to be persisted to disk
|
151
|
-
(@values.keys - data.keys).each { |key_name| self[key_name] }
|
152
165
|
resolved = {}
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
resolved.
|
158
|
-
else
|
159
|
-
setting_name = [name, data_name.to_s].join('.')
|
160
|
-
resolved[setting_name] = self[data_name] if be_resolved?(setting_name, filter)
|
166
|
+
# Resolve the settings
|
167
|
+
settings.values.each do |setting|
|
168
|
+
setting_name = setting.qualified_name
|
169
|
+
if be_resolved?(setting_name, filter)
|
170
|
+
resolved[setting_name] = setting.value.nil? ? setting.default : setting.value
|
161
171
|
end
|
162
172
|
end
|
173
|
+
# Resolve the mounts
|
174
|
+
@mounts.values.each { |mount| resolved.merge!(mount.resolve(filter)) }
|
163
175
|
resolved
|
164
176
|
end
|
165
177
|
|
@@ -208,16 +220,38 @@ module PDK
|
|
208
220
|
name.start_with?(filter + '.') # If name is a subkey of the filter then it should be resolved
|
209
221
|
end
|
210
222
|
|
211
|
-
# @abstract Subclass and override {#
|
223
|
+
# @abstract Subclass and override {#parse_file} to implement parsing logic
|
212
224
|
# for a particular config file format.
|
213
225
|
#
|
214
226
|
# @param data [String] The content of the file to be parsed.
|
215
227
|
# @param filename [String] The path to the file to be parsed.
|
216
228
|
#
|
217
|
-
# @
|
229
|
+
# @yield [String, Object] the data to be loaded into the
|
218
230
|
# namespace.
|
219
|
-
def
|
220
|
-
|
231
|
+
def parse_file(_filename); end
|
232
|
+
|
233
|
+
# @abstract Subclass and override {#serialize_data} to implement generating
|
234
|
+
# logic for a particular config file format.
|
235
|
+
#
|
236
|
+
# @param data [Hash{String => Object}] the data stored in the namespace
|
237
|
+
#
|
238
|
+
# @return [String] the serialized contents of the namespace suitable for
|
239
|
+
# writing to disk.
|
240
|
+
def serialize_data(_data); end
|
241
|
+
|
242
|
+
# @abstract Subclass and override {#create_missing_setting} to implement logic
|
243
|
+
# when a setting is dynamically created, for example when attempting to
|
244
|
+
# set the value of an unknown setting
|
245
|
+
#
|
246
|
+
# @param data [Hash{String => Object}] the data stored in the namespace
|
247
|
+
#
|
248
|
+
# @return [String] the serialized contents of the namespace suitable for
|
249
|
+
# writing to disk.
|
250
|
+
def create_missing_setting(key, initial_value = nil)
|
251
|
+
# Need to use `@settings` and `@mounts` here to stop recursive calls
|
252
|
+
return unless @mounts[key.to_s].nil?
|
253
|
+
return unless @settings[key.to_s].nil?
|
254
|
+
@settings[key.to_s] = PDK::Config::Setting.new(key.to_s, self, initial_value)
|
221
255
|
end
|
222
256
|
|
223
257
|
# Set the value of the named key.
|
@@ -228,40 +262,32 @@ module PDK
|
|
228
262
|
# @param key [String,Symbol] the name of the configuration value.
|
229
263
|
# @param value [Object] the value of the configuration value.
|
230
264
|
def set_volatile_value(key, value)
|
231
|
-
|
232
|
-
|
233
|
-
|
265
|
+
# Need to use `settings` here to force the backing file to be loaded
|
266
|
+
return create_missing_setting(key, value) if settings[key.to_s].nil?
|
267
|
+
# Need to use `@settings` here to stop recursive calls from []=
|
268
|
+
@settings[key.to_s].value = value
|
234
269
|
end
|
235
270
|
|
236
|
-
#
|
271
|
+
# Helper method to read files.
|
237
272
|
#
|
238
273
|
# @raise [PDK::Config::LoadError] if the file is removed during read.
|
239
274
|
# @raise [PDK::Config::LoadError] if the user doesn't have the
|
240
275
|
# permissions needed to read the file.
|
241
276
|
# @return [String,nil] the contents of the file or nil if the file does
|
242
277
|
# not exist.
|
243
|
-
def load_data
|
244
|
-
return if
|
245
|
-
return unless PDK::Util::Filesystem.file?(
|
278
|
+
def load_data(filename)
|
279
|
+
return if filename.nil?
|
280
|
+
return unless PDK::Util::Filesystem.file?(filename)
|
246
281
|
|
247
282
|
PDK::Util::Filesystem.read_file(file)
|
248
283
|
rescue Errno::ENOENT => e
|
249
284
|
raise PDK::Config::LoadError, e.message
|
250
285
|
rescue Errno::EACCES
|
251
286
|
raise PDK::Config::LoadError, _('Unable to open %{file} for reading') % {
|
252
|
-
file:
|
287
|
+
file: filename,
|
253
288
|
}
|
254
289
|
end
|
255
290
|
|
256
|
-
# @abstract Subclass and override {#save_data} to implement generating
|
257
|
-
# logic for a particular config file format.
|
258
|
-
#
|
259
|
-
# @param data [Hash{String => Object}] the data stored in the namespace
|
260
|
-
#
|
261
|
-
# @return [String] the serialized contents of the namespace suitable for
|
262
|
-
# writing to disk.
|
263
|
-
def serialize_data(_data); end
|
264
|
-
|
265
291
|
# Persist the contents of the namespace to disk.
|
266
292
|
#
|
267
293
|
# Directories will be automatically created and the contents of the
|
@@ -289,35 +315,17 @@ module PDK
|
|
289
315
|
|
290
316
|
# Memoised accessor for the loaded data.
|
291
317
|
#
|
292
|
-
# @return [Hash<String =>
|
293
|
-
def
|
294
|
-
|
295
|
-
@
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
# not exist.
|
302
|
-
#
|
303
|
-
# If the value has been pre-configured with {#value} to have a default
|
304
|
-
# value, resolve the default value and set it in the namespace and optionally
|
305
|
-
# save the new default.
|
306
|
-
# Otherwise, set the value to a new Hash to allow for arbitrary level of nested values.
|
307
|
-
#
|
308
|
-
# @return [Proc] suitable for use by {Hash#default_proc}.
|
309
|
-
def default_config_value
|
310
|
-
->(hash, key) do
|
311
|
-
if @values.key?(key) && @values[key].default?
|
312
|
-
set_volatile_value(key, @values[key].default)
|
313
|
-
save_data if @persistent_defaults
|
314
|
-
hash[key]
|
315
|
-
else
|
316
|
-
hash[key] = {}.tap do |h|
|
317
|
-
h.default_proc = default_config_value
|
318
|
-
end
|
319
|
-
end
|
318
|
+
# @return [Hash<String => PDK::Config::Setting>] the contents of the namespace.
|
319
|
+
def settings
|
320
|
+
return @settings if @loaded_from_file
|
321
|
+
@loaded_from_file = true
|
322
|
+
return @settings if file.nil?
|
323
|
+
parse_file(file) do |key, parsed_setting|
|
324
|
+
# Create a settings chain if a setting already exists
|
325
|
+
parsed_setting.previous_setting = @settings[key] unless @settings[key].nil?
|
326
|
+
@settings[key] = parsed_setting
|
320
327
|
end
|
328
|
+
@settings
|
321
329
|
end
|
322
330
|
end
|
323
331
|
end
|