Skip to content

Commit

Permalink
Merge e8cb392 into c04a397
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed Feb 15, 2019
2 parents c04a397 + e8cb392 commit 96ae73f
Show file tree
Hide file tree
Showing 12 changed files with 567 additions and 254 deletions.
37 changes: 37 additions & 0 deletions src/lib/y2configuration_management/salt/form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
require "y2configuration_management/salt/form_element_locator"
require "y2configuration_management/salt/form_element_factory"
require "y2configuration_management/salt/form_element_helpers"
require "y2configuration_management/salt/form_data_reader"

module Y2ConfigurationManagement
module Salt
Expand Down Expand Up @@ -225,6 +226,20 @@ def find_element_by(arg)
nil
end

# Returns default data
#
# @return [FormData]
def default_data
FormDataReader.new(self, default).form_data
end

# Default values for included elements
#
# @return [Hash]
def default
elements.reduce({}) { |a, e| a.merge(e.id => e.default) }
end

private

# @param spec [Hash] form element specification
Expand Down Expand Up @@ -274,6 +289,8 @@ def initialize(id, spec, parent:)
# @return [FormElement, nil]
# @see Form#find_element_by
def find_element_by(arg)
return self if arg.any? { |k, v| public_send(k) == v }

Array(prototype).each do |element|
nested_element = element.find_element_by(arg)
return nested_element if nested_element
Expand Down Expand Up @@ -316,6 +333,26 @@ def keyed_scalar?
scalar? && prototype.type == :key_value
end

# Returns default data
#
# @return [FormData]
def default_data
FormDataReader.new(self, default).form_data
end

# Returns the default value for the prototype
#
# @return [FormData]
def prototype_default_data
if keyed_scalar?
FormDataReader.new(self, prototype.default || value).form_data.first
elsif simple_scalar?
FormDataReader.new(self, [prototype.default]).form_data.first
else
FormDataReader.new(prototype, prototype.default).form_data
end
end

private

# Return a single or group of {FormElement}s based on the prototype given
Expand Down
21 changes: 16 additions & 5 deletions src/lib/y2configuration_management/salt/form_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ def initialize(formula)
# Renders the main form's dialog
def show_main_dialog
form_widget = form_builder.build(form.root.locator)
form_widget.value = get(form.root.locator)
root_data = get(form.root.locator)
root_data = default_for(form.root.locator) if root_data.empty?
form_widget.value = root_data.value
state.open_form(form.root.locator, form_widget)
Yast::Wizard.CreateDialog
ret = Yast::CWM.show(
Expand Down Expand Up @@ -132,7 +134,7 @@ def form_builder

# Refreshes the most recently open form widget
def refresh_top_form
state.form_widget.refresh(get(state.locator))
state.form_widget.refresh(get(state.locator).value)
state.form_widget.update_visibility(form_data)
end

Expand All @@ -150,7 +152,7 @@ def show_popup(widget)
def next_handler
state.form_widget.store
form_data.update(form.root.locator, state.form_widget.result)
pillar.data = form_data.to_h.fetch("root", {})
pillar.data = form_data.to_pillar_data(formula.form).fetch("root", {})
puts pillar.dump
true
end
Expand All @@ -168,7 +170,7 @@ def add_or_edit_item(action, relative_locator)
item_locator = new_item_locator_for_action(action, relative_locator)
form_widget = form_builder.build(item_locator)
state.open_form(item_locator, form_widget)
form_widget.value = find_or_create_item(item_locator)
form_widget.value = find_or_create_item(item_locator).value
result = show_popup(form_widget)
form_data.update(state.locator, result) unless result.nil?
state.close_form(rollback: result.nil?)
Expand All @@ -191,7 +193,7 @@ def update_parent
def find_or_create_item(item_locator)
new_item = get(item_locator)
return new_item if new_item
new_item = {}
new_item = default_for(item_locator)
form_data.add_item(item_locator.parent, new_item)
new_item
end
Expand Down Expand Up @@ -227,6 +229,15 @@ def form
def pillar
formula.pillar
end

# Default for a given locator
#
# @param locator [FormElementLocator] Form element locator
# @return [FormData]
def default_for(locator)
element = form.find_element_by(locator: locator.unbounded)
element.is_a?(Collection) ? element.prototype_default_data : element.default_data
end
end
end
end
163 changes: 68 additions & 95 deletions src/lib/y2configuration_management/salt/form_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,44 @@
# find current contact information at www.suse.com.

require "y2configuration_management/salt/form_data_reader"
require "y2configuration_management/salt/form_data_writer"

module Y2ConfigurationManagement
module Salt
# This class holds data for a given Salt Formula Form
class FormData
# @return [Y2ConfigurationManagement::Salt::Form] Form
attr_reader :form

class << self
# @param form [Form] Form definition
# @param pillar [Pillar] Pillar to read the data from
# @return [FormData] Form data merging defaults and pillar values
def from_pillar(form, pillar)
FormDataReader.new(form, pillar).form_data
reader = FormDataReader.new(form.root, pillar.data)
FormData.new(form.root.id => reader.form_data.value)
end
end

# Constructor
#
# @param form [Form] Form definition
# @param initial [Hash] Initial data in hash form
def initialize(form, initial = {})
@form = form
@data = initial
def initialize(initial)
@data = initial || {}
end

# Returns the value of a given element
#
# @param locator [FormElementLocator] Locator of the element
# @return [FormData,nil] Form data or nil if no data was found for the given locator
def get(locator)
value = find_by_locator(@data, locator)
value = default_for(locator) if value.nil?
value
value ? FormData.new(value) : nil
end

# Updates an element's value
#
# @param locator [FormElementLocator] Locator of the collection
# @param value [Object] New value
def update(locator, value)
parent = get(locator.parent)
parent = find_by_locator(@data, locator.parent)
parent[key_for(locator.last)] = value
end

Expand All @@ -67,37 +64,74 @@ def update(locator, value)
# @param locator [FormElementLocator] Locator of the collection
# @param value [Object] Value to add
def add_item(locator, value)
collection = get(locator)
collection = find_by_locator(@data, locator)
collection.push(value)
end

# @param locator [FormElementLocator] Locator of the collection
# @param value [Object] New value
def update_item(locator, value)
collection = get(locator.parent)
collection = find_by_locator(@data, locator.parent)
collection[key_for(locator.last)] = value
end

# Removes an element from a collection
#
# @param locator [FormElementLocator] Locator of the collection
def remove_item(locator)
collection = get(locator.parent)
collection = find_by_locator(@data, locator.parent)
collection.delete_at(locator.last)
end

# Returns a hash containing the form data
# Returns the stored data in raw form
#
# @return [Hash,Array]
def value
@data
end

# Returns a hash containing the information to be used in a data pillar
#
# @return [Hash]
def to_h
data_for_pillar(@data)
def to_pillar_data(form)
FormDataWriter.new(form, self).to_pillar_data
end

# Returns a copy of this object
#
# @return [FormData]
def copy
self.class.new(form, Marshal.load(Marshal.dump(@data)))
self.class.new(Marshal.load(Marshal.dump(@data)))
end

# Merges the data from another FormData instance
#
# @param other [FormData] Form data to merge with. In case of conflict, the data from
# this object has precedence.
# @return [FormData] Form data containing the merged information
def merge(other)
FormData.new(simple_merge(value, other.value))
end

# Determines whether the instance is data
#
# @return [Boolean]
def empty?
@data.is_a?(Enumerable) ? @data.empty? : false
end

# Returns the number of included elements
#
# @return [Integer]
def size
@data.is_a?(Enumerable) ? @data.size : 1
end

# Returns the first element
#
# @return [FormData]
def first
FormData.new(@data.first)
end

private
Expand All @@ -120,90 +154,29 @@ def find_by_locator(data, locator)
find_by_locator(next_data, locator.rest)
end

# Default value for a given element
#
# @param locator [FormElementLocator] Element locator
def default_for(locator)
element = form.find_element_by(locator: locator)
element ? element.default : nil
end

# Returns data in a format to be used by the Pillar
#
# @param data [Object]
# @return [Object]
def data_for_pillar(data)
case data
when Array
collection_for_pillar(data)
when Hash
hash_for_pillar(data)
else
data
end
end

# Recursively converts a hash into one suitable to be used in a Pillar
#
# @param data [Hash]
# @return [Hash]
def hash_for_pillar(data)
data.reduce({}) do |all, (k, v)|
value = data_for_pillar(v)
next all if value.nil?
all.merge(k.to_s => value)
end
end

# Converts a collection to be used in a Pillar
#
# Arrays containing hashes with a `$key` element will be converted into a hash
# using the `$key` values as hash keys. See #hash_collection_for_pillar.
#
# @param collection [Array]
# @return [Array,Hash]
def collection_for_pillar(collection)
first = collection.first
return [] if first.nil?
if first.respond_to?(:key?) && first.key?("$key")
hash_collection_for_pillar(collection)
elsif first.respond_to?(:key?) && first.key?("$value")
scalar_collection_for_pillar(collection)
else
collection.map { |d| data_for_pillar(d) }
end
end

# Converts a collection into a hash to be used in a Pillar
#
# @param collection [Array<Hash>] This method expects an array containing hashes which include
# `$key` element.
# @return [Array,Hash]
def hash_collection_for_pillar(collection)
collection.reduce({}) do |all, item|
new_item = item.clone
key = new_item.delete("$key")
val = new_item.delete("$value") || data_for_pillar(new_item)
all.merge(key => val)
end
end

# Converts a collection into an array to be used in a Pillar
#
# @param collection [Array<Hash>] This method expects an array containing hashes which include
# `$value` element.
# @return [Array]
def scalar_collection_for_pillar(collection)
collection.map { |i| i["$value"] }
end

# Convenience method which converts a value to be used as key for a array or a hash
#
# @param key [String,Symbol,Integer]
# @return [String,Integer]
def key_for(key)
key.is_a?(Symbol) ? key.to_s : key
end

# Simple deep merge
#
# @param a_hash [Hash] Default values
# @param another_hash [Hash] Pillar data
# @return [Hash]
def simple_merge(a_hash, another_hash)
a_hash.reduce({}) do |all, (k, v)|
next all.merge(k => v) if another_hash[k].nil?
if v.is_a?(Hash)
all.merge(k => simple_merge(a_hash[k], another_hash[k]))
else
all.merge(k => another_hash[k])
end
end
end
end
end
end

0 comments on commit 96ae73f

Please sign in to comment.