Skip to content

Commit

Permalink
Merge 9ab3872 into 4774158
Browse files Browse the repository at this point in the history
  • Loading branch information
mvidner committed Jan 21, 2019
2 parents 4774158 + 9ab3872 commit 0b29d11
Show file tree
Hide file tree
Showing 18 changed files with 406 additions and 19 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

# 2 space indentation
[*.rb]
indent_style = space
indent_size = 2
4 changes: 4 additions & 0 deletions src/lib/y2configuration_management/salt/form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

require "yaml"
require "yast"
require "y2configuration_management/salt/form_condition"
require "y2configuration_management/salt/form_element_locator"

module Y2ConfigurationManagement
Expand Down Expand Up @@ -120,6 +121,8 @@ class FormElement
attr_reader :scope
# @return [Boolean]
attr_reader :optional
# @return [FormCondition,nil]
attr_reader :visible_if

# Constructor
#
Expand All @@ -133,6 +136,7 @@ def initialize(id, spec, parent:)
@scope = spec.fetch("$scope", "system").to_sym
@optional = spec["$optional"] if spec["$optional"]
@parent = parent
@visible_if = FormCondition.parse(spec.fetch("$visibleIf", ""), context: locator)
end

# Return the absolute locator of this form element in the actual form
Expand Down
3 changes: 1 addition & 2 deletions src/lib/y2configuration_management/salt/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def build(form_element)
elements = scalar ? [form_element] : form_element.elements
widgets = Array(elements).map { |e| build_element(e) }
Y2ConfigurationManagement::Widgets::Form.new(
widgets, scalar: scalar
widgets, controller, scalar: scalar
)
end

Expand Down Expand Up @@ -84,7 +84,6 @@ def build_group(group)
children = group.elements.map do |element_spec|
build_element(element_spec)
end
_visible = group.type == :group # FIXME: use this
Y2ConfigurationManagement::Widgets::Group.new(group, children)
end

Expand Down
94 changes: 94 additions & 0 deletions src/lib/y2configuration_management/salt/form_condition.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright (c) [2019] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

module Y2ConfigurationManagement
module Salt
# A boolean condition operating on a value in the form,
# used for widget visibility ($visibleIf).
class FormCondition
class ParseError < RuntimeError
end

# @param s [String]
# @param context [FormElementLocator] for resolving relative expressions
def self.parse(s, context:)
if s.empty?
nil
# This matches checkVisibilityCondition in FormulaComponentGenerator.js
# TODO: specify it better
elsif s.include?("==")
parts = s.split("==").map(&:strip)
locator = parse_locator(parts[0].strip, context)
value = parse_value(parts[1].strip)
EqualCondition.new(locator: locator, value: value)
elsif s.include?("!=")
parts = s.split("!=").map(&:strip)
locator = parse_locator(parts[0], context)
value = parse_value(parts[1])
NotEqualCondition.new(locator: locator, value: value)
else
raise ParseError, "Expecting equality or inequality: #{s.inspect}"
end
end

def self.parse_locator(s, context)
if s.start_with? "."
while s.start_with? "."
s = s[1..-1]
context = context.parent
end
else
context = FormElementLocator.new(["root"])
end
s_parts = s.split "#"
context.join(* s_parts)
end

def self.parse_value(s)
if (s[0] == "'" && s[-1] == "'") || (s[0] == "\"" && s[-1] == "\"")
s[1..-2]
else
s
end
end
end

# A {FormCondition} checking if a widget is equal to a constant
class EqualCondition < FormCondition
def initialize(locator:, value:)
@locator = locator
@value = value
end

# @param data [FormData]
def evaluate(data)
left = data.get(@locator).to_s
right = @value.to_s
left == right
end
end

# A {FormCondition} checking if a widget is not equal to a constant
class NotEqualCondition < EqualCondition
def evaluate(data)
!super
end
end
end
end
9 changes: 9 additions & 0 deletions src/lib/y2configuration_management/salt/form_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ def remove(relative_locator)
refresh_top_form
end

def update_visibility
state.form_widget.store
form_result = state.form_widget.result
local_data = FormData.new(form)
local_data.update(state.locator, form_result)
state.form_widget.update_visibility(local_data)
end

private

# @return [Form]
Expand All @@ -133,6 +141,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.update_visibility(data)
end

# Displays a popup
Expand Down
4 changes: 3 additions & 1 deletion src/lib/y2configuration_management/salt/form_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def initialize(form, pillar = Pillar.new(data: {}))
#
# @param locator [String] Locator of the element
def get(locator)
find_by_locator(@data, locator) || default_for(locator)
value = find_by_locator(@data, locator)
value = default_for(locator) if value.nil?
value
end

# Updates an element's value
Expand Down
2 changes: 2 additions & 0 deletions src/lib/y2configuration_management/widgets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# 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/widgets/invisibility_cloak"

require "y2configuration_management/widgets/base_mixin"
require "y2configuration_management/widgets/boolean"
require "y2configuration_management/widgets/collection"
Expand Down
39 changes: 37 additions & 2 deletions src/lib/y2configuration_management/widgets/boolean.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,60 @@ module Y2ConfigurationManagement
# This module contains the widgets which are used to display forms for Salt formulas
module Widgets
# This class represents a boolean (checkbox) field. TODO: is tristate possible?
class Boolean < ::CWM::CheckBox
class Boolean < ::CWM::ReplacePoint
include BaseMixin

# @return [Boolean] Default value
attr_reader :default

extend Forwardable
def_delegators :@inner, :value, :value=

include InvisibilityCloak

# A helper to go inside a ReplacePoint
class CheckBox < ::CWM::CheckBox
# @return [String] Widget label
attr_reader :label

def initialize(id:, label:)
self.widget_id = id
@label = label
end

# TODO: only if I am mentioned in a visible_if
def opt
[:notify]
end
end

# Constructor
#
# @param spec [Y2ConfigurationManagement::Salt::FormElement] Element specification
def initialize(spec)
initialize_base(spec)
@default = spec.default == true # nil -> false
self.widget_id = "boolean:#{spec.id}"

@inner = CheckBox.new(id: "boolean:#{spec.id}", label: spec.label)
super(id: "vis:#{spec.id}", widget: @inner)
initialize_invisibility_cloak(spec.visible_if)
end

# @see CWM::AbstractWidget
def init
replace(@inner)
self.value = default
end

# Fixup for CWM::ReplacePoint which defaults to non unique ids
# @return [UITerm]
def contents
# In `contents` we must use an Empty Term, otherwise CWMClass
# would see an {AbstractWidget} and handle events itself,
# which result in double calling of methods like {handle} or {store} for
# initial widget.
ReplacePoint(Id(widget_id), Empty(Id("empty:#{widget_id}")))
end
end
end
end
2 changes: 2 additions & 0 deletions src/lib/y2configuration_management/widgets/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class Collection < ::CWM::CustomWidget
# @return [Array<Object>] List of objects which are included in the collection
attr_accessor :value

include InvisibilityCloak

# @return [Array<CWM::AbstractWidget>] Parent widget
attr_accessor :parent

Expand Down
16 changes: 15 additions & 1 deletion src/lib/y2configuration_management/widgets/form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,13 @@ class Form < ::CWM::CustomWidget
#
# @param children [Array<CWM::AbstractWidget>] Widgets included in the form
# @param scalar [Boolean] Determines whether the form stores are scalar value
def initialize(children, scalar: false)
def initialize(children, controller, scalar: false)
@value = scalar ? nil : {}
@scalar = scalar
add_children(*children)
@controller = controller
self.handle_all_events = true
super()
end

# This method propagates the values to the underlying widgets.
Expand Down Expand Up @@ -112,6 +115,17 @@ def refresh(values)
set_children_contents
end

def handle
@controller.update_visibility
nil
end

def update_visibility(data)
children.each do |widget|
widget.update_visibility(data)
end
end

# Add children widgets
#
# @param widgets [Array<CWM::AbstractWidget>] Widgets to add to the form
Expand Down
14 changes: 13 additions & 1 deletion src/lib/y2configuration_management/widgets/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Group < ::CWM::CustomWidget
def initialize(spec, children)
textdomain "configuration_management"
initialize_base(spec)
@has_frame = spec.type == :group
self.widget_id = "group:#{spec.id}"
add_children(*children)
end
Expand All @@ -43,7 +44,12 @@ def initialize(spec, children)
#
# @return [Yast::Term]
def contents
VBox(*children)
c = VBox(*children)
if @has_frame
Frame(label, c)
else
c
end
end

# Sets the value for the form
Expand Down Expand Up @@ -73,6 +79,12 @@ def value
children.reduce({}) { |a, e| a.merge(e.id => e.value) }
end

def update_visibility(data)
children.each do |widget|
widget.update_visibility(data)
end
end

# Add children widgets
#
# @param widgets [Array<CWM::AbstractWidget>] Widgets to add to the group
Expand Down
67 changes: 67 additions & 0 deletions src/lib/y2configuration_management/widgets/invisibility_cloak.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright (c) [2019] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "cwm"

module Y2ConfigurationManagement
module Widgets
# Widgets that can become invisible via the {visible=} method.
# TODO: the value is discarded; check how it feels, maybe we want to save/restore it.
module VisibilitySwitching
EMPTY_WIDGET = CWM::Empty.new("ic_empty")

attr_reader :visible

def initialize_visibility_switching
@visible = true
end

def visible=(visible)
return if @visible == visible
@visible = visible
if visible
replace(@inner)
else
replace(EMPTY_WIDGET)
end
end
end

# Salt forms specific visibility switching
module InvisibilityCloak
include VisibilitySwitching

# @return [FormCondition,nil]
attr_reader :visible_if

def initialize_invisibility_cloak(visible_if)
initialize_visibility_switching
@visible_if = visible_if
end

# Automatic invisibility: when the form controller asks us,
# we evaluate a condition and update our visibility
# @param data [FormData]
def update_visibility(data)
return unless @visible_if
self.visible = @visible_if.evaluate(data)
end
end
end
end

0 comments on commit 0b29d11

Please sign in to comment.