Skip to content

Commit

Permalink
Merge pull request #258 from joseivanlopez/api-changes
Browse files Browse the repository at this point in the history
API changes
  • Loading branch information
joseivanlopez committed Apr 30, 2021
2 parents 182adba + 614d966 commit b724476
Show file tree
Hide file tree
Showing 5 changed files with 491 additions and 172 deletions.
176 changes: 165 additions & 11 deletions src/lib/y2users/config.rb
Expand Up @@ -18,9 +18,23 @@
# find current contact information at www.suse.com.

module Y2Users
# Holds references to elements of user configuration like users or groups.
# Class itself holds references to different configuration instances.
# TODO: write example
# Class to represent a configuration of users and groups
#
# @example
# user1 = User.new("john")
# user2 = User.new("peter")
# group = Group.new("users")
#
# config1 = Config.new("config1")
# config1.users #=> []
# config1.attach(user1, user2, group)
# config1.users #=> [user1, user2]
# config1.groups #=> [group]
#
# config2 = config1.clone_as("config2")
# user = config2.users.first
# config2.detach(user)
# config2.users #=> [user2]
class Config
class << self
def get(name)
Expand Down Expand Up @@ -48,31 +62,171 @@ def system(reader: nil, force_read: false)
reader = Linux::Reader.new
end

# TODO: make system config immutable, so it cannot be modified directly
res = new(:system)
reader.read_to(res)

res
end
end

# Config name
#
# @return [String]
attr_reader :name
attr_accessor :users
attr_accessor :groups

def initialize(name, users: [], groups: [])
# Constructor
#
# param name [String]
def initialize(name)
@name = name
@users = users
@groups = groups

@users_manager = ElementManager.new(config)
@groups_manager = ElementManager.new(config)

self.class.register(self)
end

# Users that belong to this config
#
# @note The list of users cannot be modified directly. Use {#attach} and {#detach} instead.
#
# @return [Array<User>]
def users
users_manager.elements.dup.freeze
end

# Groups that belong to this config
#
# @note The list of groups cannot be modified directly. Use {#attach} and {#detach} instead.
#
# @return [Array<Group>]
def groups
groups_manager.elements.dup.freeze
end

# Attaches users and groups to this config
#
# The given users and groups cannot be already attached to a config.
#
# @param elements [Array<User, Group>]
def attach(*elements)
elements.each { |e| attach_element(e) }
end

# Detaches users and groups from this config
#
# @param elements [Array<User, Group>]
def detach(*elements)
elements.each { |e| detach_element(e) }
end

# Generates a new config with the very same list of users and groups
#
# Note that the cloned users and groups keep the same id as the original users and groups.
#
# @param name [String] name for the new cloned config
# @return [Config]
def clone_as(name)
config = self.class.new(name)
config.users = users.map { |u| u.clone_to(config) }
config.groups = groups.map { |g| g.clone_to(config) }

elements = users + groups
elements.each { |e| config.clone_element(e) }

config
end

protected

# Clones a given user or group and attaches it into this config
#
# Note that the cloned element keep the same id as the source element.
#
# @param element [User, Group]
def clone_element(element)
cloned = element.clone
cloned.assign_internal_id(element.id)

attach(cloned)
end

private

# Manager for users
#
# @return [ElementManager]
attr_reader :users_manager

# Manager for groups
#
# @return [ElementManager]
attr_reader :groups_manager

# Generates the id for the next attached user or group
#
# @return [Integer]
def self.next_element_id
@last_element_id ||= 0
@last_element_id += 1
end

private_class_method(:next_element_id)

# Attaches an user or group
#
# An id is assigned to the given user/group, if needed.
#
# @param element [User, Group]
def attach_element(element)
element.assign_internal_id(self.class.next_element_id) if element.id.nil?

element.is_a?(User) ? users_manager.attach(element) : groups_manager.attach(element)
end

# Detaches an user or group
#
# @param element [User, Group]
def detach_element(element)
element.is_a?(User) ? users_manager.detach(element) : groups_manager.detach(element)
end

# Helper class to manage a list of users or groups
class ElementManager
# @return [Array<User, Group>]
attr_reader :elements

# Constructor
#
# @param config [Config]
def initialize(config)
@config = config
@elements = []
end

# Attaches the element to the config
#
# @raise [RuntimeError] if the element is already attached
#
# @param element [User, Group]
def attach(element)
raise "Element already attached: #{element}" if element.attached?

@elements << element

element.assign_config(config)
end

# Detaches the element from the config
#
# @param element [User, Group]
def detach(element)
return if element.config != config

index = @elements.find_index { |e| e.is?(element) }
@elements.delete_at(index) if index

element.assing_config(nil)
element.assign_internal_id(nil)
end
end
end
end
92 changes: 92 additions & 0 deletions src/lib/y2users/config_element.rb
@@ -0,0 +1,92 @@
# Copyright (c) [2021] 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 Y2Users
# Methods for an element that can be attached to a {Config} (e.g., {User}, {Group}).
module ConfigElement
# {Config} in which the element is attached to
#
# @return [Config, nil] nil if the element is not attached yet
attr_reader :config

# Internal identifier to distinguish elements
#
# Two elements are considered to be the same if they have the same id, even when they live in
# different configs.
#
# @return [Integer, nil] the id is assigned by the config when attaching the element
attr_reader :id

# Assigns the internal id for this element
#
# @note The id of an element should not be modified. This method is exposed in the public API
# only to make possible to set the id when attaching/detaching an element to/from a config,
# see {Config#attach} and {Config#detach}.
#
# @param id [Integer]
def assign_internal_id(id)
@id = id
end

# Assigns the config which the element belongs to
#
# @note The config of an element should not be modified. This method is exposed in the public
# API only to make possible to set the config reference when attaching/detaching an element
# to/from a config, see {Config#attach} and {Config#detach}.
#
# @param config [Config]
def assign_config(config)
@config = config
end

# Whether the element is currently attached to a {Config}
#
# @return [Boolean]
def attached?
!config.nil?
end

# Whether this element is considered the same as other
#
# Two elements are considered the same when they have the same id, independently on the rest of
# attributes.
#
# @param other [User, Group]
# @return [Boolean]
def is?(other)
return false unless self.class == other.class
return false if id.nil? || other.id.nil?

id == other.id
end

# Generates a new cloned element without an specific config or id.
#
# Note that the new cloned element is not attached to any config.
#
# @return [User, Group]
def clone
cloned = super
cloned.config = nil
cloned.id = nil

cloned
end
end
end

0 comments on commit b724476

Please sign in to comment.