Skip to content

Commit

Permalink
Merge ebf2bdb into 7c679c4
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed Jan 23, 2019
2 parents 7c679c4 + ebf2bdb commit 36d1463
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 100 deletions.
34 changes: 22 additions & 12 deletions src/lib/y2configuration_management/salt/form_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ class FormController
# @param form [Y2ConfigurationManagement::Salt::Form]
# @param pillar [Y2ConfigurationManagement::Salt::Pillar]
def initialize(form, pillar)
@data = FormData.new(form, pillar)
@form = form
@pillar = pillar
@state = FormControllerState.new
data = FormData.from_pillar(form, pillar)
@state = FormControllerState.new(data)
end

# Renders the main form's dialog
Expand All @@ -83,7 +83,7 @@ def show_main_dialog
#
# @param locator [String] Locator of the element
def get(locator)
@data.get(locator)
form_data.get(locator)
end

# Opens a new dialog in order to add a new element to a collection
Expand All @@ -105,7 +105,7 @@ def edit(relative_locator)
# @param relative_locator [FormElementLocator] Elements's locator
def remove(relative_locator)
locator = state.locator.join(relative_locator)
@data.remove_item(locator)
form_data.remove_item(locator)
refresh_top_form
end

Expand Down Expand Up @@ -149,8 +149,8 @@ def show_popup(widget)
# version.
def next_handler
state.form_widget.store
data.update(form.root.locator, state.form_widget.result)
pillar.data = data.to_h.fetch("root", {})
form_data.update(form.root.locator, state.form_widget.result)
pillar.data = form_data.to_h.fetch("root", {})
puts pillar.dump
true
end
Expand All @@ -167,7 +167,7 @@ def add_or_edit_item(action, relative_locator)
add_or_update_parent
result = run_popup(action, relative_locator)
update_form_data(result)
state.close_form
state.close_form(rollback: result.nil?)
refresh_top_form
end

Expand All @@ -186,13 +186,16 @@ def run_popup(action, relative_locator)

# Updates the form data depending on the action
#
# When result is `nil`, it just restores the backup.
#
# @param result [Hash,nil] Result to process
def update_form_data(result)
return if result.nil?
return nil if result.nil?

if state.action == :add
@data.add_item(state.locator, result)
form_data.add_item(state.locator, result)
else
@data.update(state.locator, result)
form_data.update(state.locator, result)
end
end

Expand All @@ -205,9 +208,9 @@ def add_or_update_parent
parent = state.form_widget.result

if state.action == :edit
@data.update(state.locator, parent)
form_data.update(state.locator, parent)
else
@data.add_item(state.locator, parent)
form_data.add_item(state.locator, parent)
locator = state.locator.join(get(state.locator).size - 1)
state.replace(:edit, locator)
end
Expand All @@ -223,6 +226,13 @@ def item_form_for(locator)
form_widget.title = element.name
form_widget
end

# Returns the current form data
#
# @return [FormData] Form data
def form_data
@state.form_data
end
end
end
end
48 changes: 43 additions & 5 deletions src/lib/y2configuration_management/salt/form_controller_state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ module Salt
# Stores the UI state information
#
# This class holds information related to FormController. Basically, it behaves like a stack
# which contains informaion about the open widget forms. This data could have been stored
# directly in the FormController instance, but it has been extracted in order to keep the
# controller as simple as possible.
# which contains informaion about the open widget forms and the current form data. Those items
# could have been stored directly in the FormController instance, but they have been extracted
# in order to keep the controller as simple as possible.
class FormControllerState
# Constructor
def initialize
#
# @param data [FormData] Initial form data
def initialize(data)
@form_widgets = []
@locators = []
@actions = []
@form_data_instances = [data]
end

# Registers that a new form has been open
Expand All @@ -46,6 +49,7 @@ def open_form(action, locator, form_widget)
@form_widgets << form_widget
@locators << locator
@actions << action
backup_data
end

# Most recently open form widget
Expand All @@ -70,16 +74,50 @@ def locator
end

# Replaces the current action/locator
#
# @param new_action [Symbol]
# @param new_locator [FormElementLocator]
def replace(new_action, new_locator)
@actions[-1] = new_action
@locators[-1] = new_locator
end

# Removes the information related to the most recently open form widget
def close_form
def close_form(rollback: false)
@form_widgets.pop
@locators.pop
@actions.pop
if rollback
restore_backup
else
remove_backup
end
end

# Current form data
#
# @return [FormData]
def form_data
@form_data_instances.last
end

private

# Performs a backup of the current form data
def backup_data
@form_data_instances << form_data.copy
end

# Restores the last backup of the current form data by removing the current snapshot
def restore_backup
@form_data_instances.pop
end

# Clears the last backup if it exists
def remove_backup
# This is another way of saying `top = @fdi.pop; @fdi.last = top`,
# or "shorten the snapshot stack but commit the last element"
@form_data_instances.delete_at(-2)
end
end
end
Expand Down
92 changes: 23 additions & 69 deletions src/lib/y2configuration_management/salt/form_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,36 @@
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "y2configuration_management/salt/pillar"
require "y2configuration_management/salt/form_data_reader"

module Y2ConfigurationManagement
module Salt
# This class holds data for a given Salt Formula Form
#
# @todo The support for collections is rather simple and nesting collections is not supported.
# We might consider using JSON Patch to modify the data.
class FormData
# @return [Y2ConfigurationManagement::Salt::Form] Form
attr_reader :form
# @return [Y2ConfigurationManagement::Salt::Pillar] Pillar
attr_reader :pillar

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

# Constructor
#
# @param form [Y2ConfigurationManagement::Salt::Form] Form
# @param pillar [Y2ConfigurationManagement::Salt::Form] Pillar
def initialize(form, pillar = Pillar.new(data: {}))
@data = data_for_form(form, pillar.data)
# @param form [Form] Form definition
# @param data [Hash] Initial data in hash form
def initialize(form, initial = {})
@form = form
@pillar = pillar
@data = initial
end

# Returns the value of a given element
#
# @param locator [String] Locator of the element
# @param locator [FormElementLocator] Locator of the element
def get(locator)
find_by_locator(@data, locator) || default_for(locator)
end
Expand Down Expand Up @@ -88,13 +91,20 @@ def to_h
data_for_pillar(@data)
end

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

private

# Recursively finds a value
#
# @param data [Hash,Array] Data structure to search for the value
# @param locator [FormElementLocator] Value locator
# @return [Object] Value
# @return [Object,nil] Found value; nil if no value was found for the given locator
def find_by_locator(data, locator)
return nil if data.nil?
return data if locator.first.nil?
Expand All @@ -116,62 +126,6 @@ def default_for(locator)
element ? element.default : nil
end

# Builds a hash to keep the form data
#
# @param form [Y2ConfigurationManagement::Salt::Form]
# @param data [Hash] Pillar data
# @return [Hash]
def data_for_form(form, data)
data_for_element(form.root, data)
end

# Builds a hash to keep the form element data
#
# @param element [Y2ConfigurationManagement::Salt::FormElement]
# @param data [Hash] Pillar data
# @return [Hash]
def data_for_element(element, data)
if element.is_a?(Container)
defaults = element.elements.reduce({}) { |a, e| a.merge(data_for_element(e, data)) }
{ element.id => defaults }
else
value = find_in_pillar_data(data, element.locator.rest) # FIXME: remove '.root'
value ||= element.default
{ element.id => data_from_pillar_collection(value, element) }
end
end

# Converts from a Pillar collection into form data
#
# Basically, a collection might be an array or a hash. The internal representation, however,
# is always an array, so it is needed to do the conversion.
#
# @param element [Y2ConfigurationManagement::Salt::FormElement]
# @param value [Array,Hash]
# @return
def data_from_pillar_collection(collection, element)
return nil if collection.nil?
return collection unless element.respond_to?(:keyed?) && element.keyed?
collection.map do |k, v|
{ "$key" => k }.merge(v)
end
end

# Finds a value within a Pillar
#
# @todo This API might be available through the Pillar class.
#
# @param data [Hash,Array] Data structure from the Pillar
# @param locator [FormElementLocator] Value locator
# @return [Object] Value
def find_in_pillar_data(data, locator)
return nil if data.nil?
return data if locator.first.nil?
key = locator.first
key = key.is_a?(Symbol) ? key.to_s : key
find_in_pillar_data(data[key], locator.rest)
end

# Returns data in a format to be used by the Pillar
#
# @param data [Object]
Expand Down

0 comments on commit 36d1463

Please sign in to comment.