From b39b510db8324981c5b2089336f01bad0cd0dd1a Mon Sep 17 00:00:00 2001 From: metalwolf Date: Tue, 11 Feb 2020 23:39:51 -0600 Subject: [PATCH] patch v0.3.0 --- LICENCE | 21 +++ README.md | 334 +++--------------------------------------------- xconfig.go | 276 ++++++++++++++++++++++++++++++++++++++- xconfig_test.go | 141 ++++++++++++++++++++ 4 files changed, 451 insertions(+), 321 deletions(-) create mode 100644 LICENCE create mode 100644 xconfig_test.go diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..38dcdeb --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Philippe Thomassigny https://github.com/webability-go/xcore + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index d6166a4..2e00b3b 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,40 @@ @UTF-8 -XConfig for GO v0 +[![Go Report Card](https://goreportcard.com/badge/github.com/webability-go/xconfig)](https://goreportcard.com/report/github.com/webability-go/xconfig) +[![GoDoc](https://godoc.org/github.com/webability-go/xconfig?status.png)](https://godoc.org/github.com/webability-go/xconfig) +[![GolangCI](https://golangci.com/badges/github.com/webability-go/xconfig.svg)](https://golangci.com) + +XConfig for GO v1 ============================= -The XConfig library is used to easily build a config object based on a descriptor file. +Package xconfig loads a configuration file similar to a .ini file, but with some important improvements: +- The xconfig recognize bool, int, float and Strings, and also collections of values and hierarchical nested sub-config sets. +- The xconfig is compatible with XDataset to inject and use in templates, database records, etc. +- You can load more than one file in merge mode or replacing mode in the same config object. +- You can set and get parameters at any time. +- You can load the config object based on a file, but also on a string and another object; this way you can serialize, unserialize, transfer by any strem, save or load on database the object, etc. +- You can also save back the config string with all its comments. + +Manuals are available on godoc.org [![GoDoc](https://godoc.org/github.com/webability-go/xconfig?status.png)](https://godoc.org/github.com/webability-go/xconfig) TO DO: ====== - Merge vs load to load more than 1 file (pending) -- implement + and : +- implement + and : * - implement Save - Add a flag when it's a multiple load to warn a "save" - Add error control con Get* for type conversions (if the type is different as expected) -- Integrate with xcore.XDataset - Version Changes Control ======================= +v0.3.0 - 2020-02-11 +----------------------- +- Documentation enhanced, Configuration Syntax Reference added +- Licence and Tests Added + v0.2.1 - 2020-02-10 ----------------------- - Documentation enhanced @@ -76,312 +91,3 @@ V0.0.2 - 2018-11-27 V0.0.1 - 2018-11-14 ----------------------- - First commit, only work to load basic parameters - - - - -Manual: -======================= - -XConfig loads a configuration file similar to a .ini file, but with some important improvements: -- You can load more than one file in merge mode or replacing mode in the same config object. -- You can set and get parameters at any time. -- You can load the config object based on a file, but also on a string and another object; this way you can serialize, unserialize, transfer by any strem, save or load on database the object, etc. -- You can also save ack the config string with all its comments - -Basic use: ----------- - -1. Installing the package: - -Execute on your operating system: - -``` -go get github.com/webagility-go/xconfig -``` - -2. Importing the package: - -Adds to the import section: - -``` -import "github.com/webability-go/xconfig" -``` - -3. The configuration files: - -3.1 General syntax - -The configuration file is a set of key=value parameters, with optional comments. - -The configuration file have the following syntax: - -``` -# this file is named example.conf, used in following examples -# the # denotes a comment. -; is also a comment -parameter1=value1 -parameter2=value2 -# Repeating the same parameter will create an array of values for this parameter -parameter2=value3 - -# Creates a sub XConfig dataset for parameter, then again for subparameter -parameter.subparameter.subparameter2=value - -# Force to add the value to the parameter (with an extra '+'): -parameter2+=value4 - -# Replace any value the parameter already has by this one (with an extra ':'): -parameter2:=value4 - -``` - -3.2 Parameter keys: - -The parameter key is a string with characters [a-zA-Z0-9_-] only, with a minimum of 1 character. -The parameters may have a + or a * after their name, to denote "add the value" or "replace the value" forced rule. -Without the + and * (in normal operation), the "add" or "replace" behaviour depends on the funcion called for loading the configuration (Load* or Merge* functions) -The point (.) denotes a sub set of parameters (a new sub XConfig dataset for this parameter) - -3.3 Parameter values: - -There are 4 types of values: -- Strings -- Integer -- Float -- Boolean - -The value has no restrictions except it must enter into the line (no line breaks allowed) -The compiler accepts strings "true", "on", "yes" as a boolean 'true' and "false", "off", "no", "none" as a boolean 'false'. -For instance, that means parameter=off is a boolean false, and parameter=yes is a boolean true in the XConfig structure. - -The compiler also convert all integers to an int parameter in the XConfig structure, and float values as float64 type. -If you want a natural integer, float or boolean interpreted as a string, you must start it with a " character: -param1="123 will be the string **123** in the XConfig structure - -If you want a string starting with a ", you will need to put 2 " at the begining: -param=""abc will be the string **"abc** in the XConfig structure - -When you insert more than one value for a parameter, it creates an array of the type of the first found value. If you have a mixed type of values, you will get an error - -for instance: -``` -parameter1=true -parameter1=123 -parameter1=hello -``` -"123" and "hello" are not boolean so you get an error reading your definition file. - -Note that is the first parameter is a string, all new values will should start with " to be considered as a string also: -``` -parameter1=hello -parameter1="true -parameter1="123 -``` -you will obtain an array []string with values ["hello", "true", "123"] - -The order IS important. - -4. Invoking the XConfig in your code: - -First of all, you need to import the library in your code: - -``` -import ( - "github.com/webagility-go/xconfig" -) -``` - -Then you need first to create a blank XConfig instance: - -``` -xc := xonfig.New() -``` - -Then, you generally load a file to fill in your XConfig definition - -``` -xc.LoadFile("/path/to/my/file.conf") -``` - -And finally use the configuration - -``` -myparam := xc.Get("myparam") -``` - -myparam will take the type of the parameter: string, integer, float64, bool, or an array of string, integer or float64 -(you should be aware of the type of your parameter before using it) - - -5. Merging vs Loading - - -Advanced use: -------------- - - - -Package reference: ------------------- - -1. New, Declaration - -2. Set - -3. Add - -4. Get - -5. LoadFile - -6. MergeFile - -7. LoadString - -8. MergeString - -9. LoadXConfig - -10. MergeXConfig - -11. Clone - - -Internals: ----------- - - - - - - - -The XConfig object is easily usable as: -``` -# Using the New function -config := xconfig.New(nil) - -# Auto-new operator -config := &xconfig.XConfig{} - -# Default new operator -config := new(xconfig.XConfig) -``` - -or, if you load your own file by other means (remote, database etc) -``` -config := &xconfig.XConfig{} -mydata := getMyParameters() // get the whole configuration file into mydata -confif.LoadString(mydata) -``` - -or, if you already have your configuration into a Map of Strings (unserialized, etc) -``` -config := &xconfig.XConfig{} -mydata := map[string]string{"param1":"value1","param2":"value2"} -confif.LoadXConfig(mydata) -``` - -There are 3 sets of public functions: -Load*: to load a file, a string dataset, or another XConfig dataset. Loading means all already existing parameters will be replaced by the new configuration. - This is usefull when you have a main config file, and a local config file that must replace some values - Functions are LoadFile, LoadString and LoadXConfig -Merge*: to merge a file, a string dataset, or another XConfig dataset. Merging means all new entries will be added to the already existing parameters. - This is userfull then you split your config file into subset of parameters each (for instance database config, memory config, internationalization config, etc) - Functions are MergeFile, MergeString and MergeXConfig -Get/Set/Add: to read, set (replace) or add (merge) parameters to the XConfig. - -Once you have an instance of your configuration, you may use it like this: - -``` -// assign a local variable -param1 := config.Get("parameter1") -fmt.Println(param1) - -// assign to an already casted local variable -var param2 string -param2 = config.Get("parameter2").(string) // be carefull that the parameter IS actually the same cast or an error is thrown -fmt.Println(param2) - -// use directly the parameters -for p, v := range config { - fmt.Printf("%s=%v\n", p, v) - -// set a new parameter -config.Set("parameter3", "value3") -config.Set("parameter3", "new value3") // will be replaced -config.Add("parameter3", "another value3") // will be replaced by an array of values with both entries into it -config.Set("parameter4", 12345) -config.Set("parameter5", true) -``` - -Advanced topics: -================ - -1. Default values: ------------------- - -You may pass a map of default values to the XCOnfig so if the parameter is *not present* into the config file, it will take the default value. -Note: default values will be taken only if the parameter DOES NOT EXIST into the config file. This means an empty value is considerated as a value - -Something like this: -parameter1= -will not fire the default value because the parameter is present into the config file - -Example: --------- - -class myConfig extends XConfig -{ - private $default = array( - 'parameter1' => 'default1' - ); - - public function __construct($data) - { - parent::__construct($data, $this->default); - } -} - -Merging vs Loading: -------------------- - -+ and : - -You may merge two config file (or more), for example when you have a master config file and a local replacement values config file: -``` -include_once 'include/xconfig/XConfig.class.php'); -$globalconfig = new XConfig(file_get_contents('myglobalconfig.conf')); -$localconfig = new XConfig(file_get_contents('mylocalconfig.conf')); -$globalconfig->merge($localconfig); -``` -with files: -``` -#global config: -ip=127.0.0.1 -port=80 -domain=test.com -``` -``` -#local config: -port=8080 -title=Welcome -``` - -The result config after merging local into global will be: -``` -ip=127.0.0.1 -port=8080 -domain=test.com -title=Welcome -``` - -Sub dataset: ------------- - - - - - ---- diff --git a/xconfig.go b/xconfig.go index 0fe0451..a77fd32 100644 --- a/xconfig.go +++ b/xconfig.go @@ -2,22 +2,79 @@ // Use of this source code is governed by a MIT licence. // license that can be found in the LICENSE file. -// Package xconfig is used to easily build a config object based on a descriptor file. +// Package xconfig loads a configuration file similar to a .ini file, but with some important improvements: // -// The parameters can be nested, you it is easy to have a sub config set into the main config file. +// - The xconfig recognize bool, int, float and Strings, and also collections of values and hierarchical nested sub-config sets. // -// Config File format reference +// - The xconfig is compatible with XDataset to inject and use in templates, database records, etc. +// +// - You can load more than one file in merge mode or replacing mode in the same config object. +// +// - You can set and get parameters at any time. +// +// - You can load the config object based on a file, but also on a string and another object; this way you can serialize, unserialize, transfer by any strem, save or load on database the object, etc. +// +// - You can also save back the config string with all its comments. +// +// +// Basic use +// +// 1. Installing the package: +// +// Execute on your operating system: +// +// go get -u github.com/webagility-go/xconfig +// +// 2. Importing the package: +// +// import "github.com/webability-go/xconfig" +// +// 3. Then you need first to create a blank XConfig instance: +// +// xc := xonfig.New() +// +// 4. Then, you generally load a file to fill in your XConfig definition +// +// xc.LoadFile("/path/to/my/file.conf") +// +// 5. And finally use the configuration +// +// myparam := xc.Get("myparam") +// +// myparam will take the type of the parameter: string, integer, float64, bool, or an array of string, integer or float64 +// (you should be aware of the type of your parameter before using it) +// +// +// File format reference // // The config file is a simple utf8 flat text file. -// Every entry is on the format: +// The configuration file is a set of key=value parameters, with optional comments. // +// The configuration file have the following syntax: +// +// # this file is named example.conf, used in following examples +// # the # denotes a comment. +// ; is also a comment // parameter1=value1 // parameter2=value2 +// # Repeating the same parameter will create an array of values for this parameter +// parameter2=value3 +// +// # Creates a sub XConfig dataset for parameter, then again for subparameter +// parameter.subparameter.subparameter2=value +// +// # Force to add the value to the parameter (with an extra '+'), in this case to the array of string values of parameter2 +// parameter2+=value4 +// +// # Replace any value the parameter already has by this one (with an extra ':'), in this case parameter2 is a string again +// parameter2:=value4 // // You can add as many as parameters you wish into the file. // -// 1. comments -// You may add comments and comment unused parameter with # +// +// 1. comments: +// +// You may add comments and also comment unused parameter with # or ; at the beginning of the line // // # This is the config file for my application // MAINPATH=/home/var @@ -25,6 +82,211 @@ // # Unused parameter: // # DOMAIN=mydomain.com // +// +// 2. Parameter keys: +// +// The parameter key is a string with characters [a-zA-Z0-9_-] only, with a minimum of 1 character. +// +// The point (.) denotes a sub set of parameters (a new sub XConfig dataset for this parameter) +// +// database.user=username +// database.pass=password +// database.db=dbname +// +// In this case the database entry of the XConfig is again another XConfig with 3 parameters into it: user, pass and db. +// +// +// 3. Assignation sign: +// +// A simple = sign is the normal assignation, the "add" or "replace" behaviour depends on the funcion called for loading the configuration (Load* or Merge* functions). +// +// In this case various asignation to the same parameter will create an array of values of the same type as the first declared parameter. +// +// An equal sign preceded by a + (+=) will always add the parameter to the array of values, never replace it (see Merge/Load). +// +// An equal sign preceded by a : (:=) will always replace the parameter and discard any already set values. +// +// +// 4. Parameter values: +// +// There are 4 types of values: +// +// - Strings +// +// - Integer +// +// - Float +// +// - Boolean +// +// The value has no restrictions except it must enter into the line (no line breaks allowed) +// The compiler accepts strings "true", "on", "yes" as a boolean 'true' and "false", "off", "no", "none" as a boolean 'false'. +// For instance, that means parameter=off is a boolean false, and parameter=yes is a boolean true in the XConfig structure. +// +// The compiler also convert all integers to an int parameter in the XConfig structure, and float values as float64 type. +// If you want a natural integer, float or boolean interpreted as a string, you must start it with a " character: +// param1="123 will be the string 123 in the XConfig structure +// +// If you want a string starting with a ", you will need to put 2 " at the begining: +// param=""abc will be the string "abc in the XConfig structure +// +// 3. list of values: +// +// You can repeat as many time you need the same parameter name with different values. +// This will build a list of values in the object. +// The list of values is kept as an array of values. +// +// If you have a mixed type of values, you will get an error +// +// for instance: +// +// # Those are booleans +// parameter1=true +// parameter2=on +// parameter3=no +// +// # Those are integers +// parameter4=0 +// parameter5=1 +// parameter6=234 +// parameter7=-5 +// parameter8=837456783456 +// +// # Those are floats +// parameter10=0.0 +// parameter11=1.7 +// parameter12=234.5 +// parameter13=-5.834 +// parameter14=837456783.456 +// parameter15=-5.834e7 +// +// # Those are strings +// parameter20=asdh +// parameter21="1 +// parameter22="false +// parameter23="-5.834 +// parameter24=""12345 +// parameter25=something 123 true false on off +// +// # This parameter will force parameter1 to become an array of booleans [true, false] +// parameter1=false +// +// # This will throw an error since parameter1 is a boolean and abc is not a boolean +// parameter1=abc +// +// # Note that is the first parameter is a string, all new values will should start with " to be considered as a string also: +// parameter30=hello +// parameter30="true +// parameter30="123 +// # you will obtain an array []string with values ["hello", "true", "123"] +// +// # List of authorized languages: +// languages=es +// languages=en +// languages=fr +// languages=jp +// +// The order IS important. +// +// Once loaded you will get a []string{“es”, “en”, “fr”, “jp”} assigned to the “languages” parameter. +// +// +// Merging vs Loading +// +/* ++ and : + +You may merge two config file (or more), for example when you have a master config file and a local replacement values config file: +``` +include_once 'include/xconfig/XConfig.class.php'); +$globalconfig = new XConfig(file_get_contents('myglobalconfig.conf')); +$localconfig = new XConfig(file_get_contents('mylocalconfig.conf')); +$globalconfig->merge($localconfig); +``` +with files: +``` +#global config: +ip=127.0.0.1 +port=80 +domain=test.com +``` +``` +#local config: +port=8080 +title=Welcome +``` + +The result config after merging local into global will be: +``` +ip=127.0.0.1 +port=8080 +domain=test.com +title=Welcome +``` +*/ +// +// Advanced use +// +// The XConfig object is easily usable as: +// +// // Using the New function +// config := xconfig.New(nil) +// +// // Auto-new operator +// config := &xconfig.XConfig{} +// +// // Default new operator +// config := new(xconfig.XConfig) +// +// or, if you load your own file by other means (remote, database etc) +// +// config := &xconfig.XConfig{} +// mydata := getMyParameters() // get the whole configuration file into mydata string +// config.LoadString(mydata) +// +// or, if you already have your configuration into a Map of Strings (unserialized, etc) +// +// config := &xconfig.XConfig{} +// mydata := map[string]string{"param1":"value1","param2":"value2"} +// config.LoadXConfig(mydata) +// +// There are 3 sets of public functions: +// +// Load*: to load a file, a string dataset, or another XConfig dataset. Loading means all already existing parameters will be replaced by the new configuration. +// +// This is usefull when you have a main config file, and a local config file that must replace some values +// Functions are LoadFile, LoadString and LoadXConfig +// +// Merge*: to merge a file, a string dataset, or another XConfig dataset. Merging means all new entries will be added to the already existing parameters. +// +// This is userfull then you split your config file into subset of parameters each (for instance database config, memory config, internationalization config, etc) +// Functions are MergeFile, MergeString and MergeXConfig +// +// Get/Set/Add: to read, set (replace) or add (merge) parameters to the XConfig. +// +// Once you have an instance of your configuration, you may use it like this: +// +// // assign a local variable +// param1 := config.Get("parameter1") +// fmt.Println(param1) +// +// // assign to an already casted local variable +// var param2 string +// param2 = config.Get("parameter2").(string) // be carefull that the parameter IS actually the same cast or an error is thrown +// fmt.Println(param2) +// +// // use directly the parameters +// for p, v := range config { +// fmt.Printf("%s=%v\n", p, v) +// } +// +// // set a new parameter +// config.Set("parameter3", "value3") +// config.Set("parameter3", "new value3") // will be replaced +// config.Add("parameter3", "another value3") // will be replaced by an array of values with both entries into it +// config.Set("parameter4", 12345) +// config.Set("parameter5", true) +// package xconfig import ( @@ -41,7 +303,7 @@ import ( ) // VERSION is the used version nombre of the XCore library. -const VERSION = "0.2.1" +const VERSION = "0.3.0" // Parameter is the basic entry parameter into the configuration object // Value is the value of the parameter. diff --git a/xconfig_test.go b/xconfig_test.go new file mode 100644 index 0000000..20bfe93 --- /dev/null +++ b/xconfig_test.go @@ -0,0 +1,141 @@ +package xconfig + +import ( + "fmt" + "testing" + + "github.com/webability-go/xcore" +) + +func TestOneStringParam(t *testing.T) { + // Test 1: assign a simple parameter string with some comments + conf := New() + conf.LoadString("#First test\nparam1=value1\n\n;End of test 1\n") + + // print what we got + fmt.Println(conf) + + // direct access + if (*conf).Parameters["param1"].Value != "value1" { + t.Errorf("The parameter param1 is not correctly set") + } + + // Get + if v, _ := conf.Get("param1"); v != "value1" { + t.Errorf("The parameter param1 is not correctly passed") + } +} + +func TestStringParam(t *testing.T) { + // Test 2: assign 3 different parameters string + conf := New() + conf.LoadString("param1=value1\nparam2=value2\nparam3=value3\nparam4=\"123\nparam5=\"on") + + // print what we got + fmt.Println(conf) + + // direct access + if (*conf).Parameters["param1"].Value != "value1" || (*conf).Parameters["param2"].Value != "value2" || (*conf).Parameters["param3"].Value != "value3" { + t.Errorf("The parameters are not correctly set") + } + if (*conf).Parameters["param4"].Value != "123" || (*conf).Parameters["param5"].Value != "on" { + t.Errorf("The parameters are not correctly set") + } + + // Get + v1, _ := conf.Get("param1") + v2, _ := conf.Get("param2") + v3, _ := conf.Get("param3") + v4, _ := conf.Get("param4") + v5, _ := conf.Get("param5") + if v1 != "value1" || v2 != "value2" || v3 != "value3" || v4 != "123" || v5 != "on" { + t.Errorf("The parameters are not correctly passed") + } +} + +func TestBoolParam(t *testing.T) { + // Test 3: assign a simple bool + conf := New() + conf.LoadString("param1=yes\nparam2=true\nparam3=on\nparam4=no\nparam5=none\nparam6=false\nparam7=off") + + fmt.Println(conf) + + if (*conf).Parameters["param1"].Value != true || (*conf).Parameters["param2"].Value != true || (*conf).Parameters["param3"].Value != true || (*conf).Parameters["param4"].Value != false || (*conf).Parameters["param5"].Value != false || (*conf).Parameters["param6"].Value != false || (*conf).Parameters["param7"].Value != false { + t.Errorf("The boolean parameters are not correctly set") + } +} + +func TestIntegerParam(t *testing.T) { + // Test 4: + conf := New() + conf.LoadString("param1=0\nparam2=-1\nparam3=1234567890") + + fmt.Println(conf) + + if (*conf).Parameters["param1"].Value != 0 || (*conf).Parameters["param2"].Value != -1 || (*conf).Parameters["param3"].Value != 1234567890 { + t.Errorf("The integer parameters are not correctly set") + } +} + +func TestFloatParam(t *testing.T) { + // Test 4: + conf := New() + conf.LoadString("param1=0.123\nparam2=12e7\nparam3=-76364.2") + + fmt.Println(conf) + + if (*conf).Parameters["param1"].Value != 0.123 || (*conf).Parameters["param2"].Value != 12e7 || (*conf).Parameters["param3"].Value != -76364.2 { + t.Errorf("The float parameters are not correctly set") + } +} + +func TestArrayParam(t *testing.T) { + // Test 5: + conf := New() + conf.LoadString("param1=value1\nparam1=value2\nparam1=value3\nparam2=123\nparam2=-1\nparam2=1234567890\nparam3=0.1\nparam3=-123.567\nparam3=12e7\nparam4=true\nparam4=off\nparam4=on") + + fmt.Println(conf) + + // arr := (*conf).Parameters["param1"].Value + + // if arr.([]string)[0] != "value1" || arr.([]string)[1] != "value2" || arr.([]string)[2] != "value3" { + // t.Errorf("The array parameter is not correctly set") + // } +} + +/* Test injection of a config into a template */ +func TestTemplate(t *testing.T) { + + tmpl, _ := xcore.NewXTemplateFromString(` +Some data: +{{param1}} +{{param2}} +{{param3>data1}} +{{param3>data2}} +{{param4}} +{{param5}} +`) + + conf := New() + conf.LoadString("param1=value1\nparam2=value2\nparam3.data1=value3-data1\nparam3.data2=value3-data2\nparam4=\"123\nparam5=\"on") + + fmt.Println(conf) + + result := tmpl.Execute(conf) + fmt.Println("Result: ", result) + +} + +func TestClone(t *testing.T) { + // Test 1: assign a simple parameter string with some comments + conf := New() + conf.LoadString("#First test\nparam1=value1\n\n;End of test 1\n") + + conf2 := conf.Clone() + conf.Set("param10", "value10") + + // print what we got + fmt.Println("ANTES DE CLONE", conf) + fmt.Println("OBJETO CLONED", conf2) + +}