Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
As it often happens with class hierarchies, we have some specific fields that was firstly thought to be common to all subclasses, but end up being relevant just for some of those. This refactoring gives a glimpse of what to do in these cases.
Working example
Our working example is a straightforward Employee class hierarchy:
classDiagram
class Employee
Employee <|-- Engineer
Employee <|-- Salesman
class Employee {
name
quota
}
Loading
Our goal is to move the quota field down to Salesman, since it’s only used there.
Test suite
Our test suite covers the basic properties of Salesman:
describe('Salesman',()=>{it('should have a name',()=>{constsalesman=newSalesman('Kaio');expect(salesman.name).toBe('Kaio');});it('should have a quota',()=>{constsalesman=newSalesman('Kaio');expect(salesman.quota).toBe(1.5);});});
That’s the minimum we need in place to get going.
Steps
We start by copying the quota field into Salesman:
This public repository is read-only and no longer maintained.
Automate Operations with CLI
Description
This example script is a companion to the tutorial “Automate Account Operations with the Command Line Interface (CLI)”.
For complete information, see the tutorial on developers.sap.com.
With this example for the CLI for SAP BTP, you can automate the setting up of new development environments.
This includes the creation of a directory, subaccounts and entitlements, as well as a Cloud Foundry space and an instance of the SAP HANA Cloud service in the space.
Requirements
You should have an account on SAP BTP.
You need to have the cf CLI installed.
Since the script is a bash shell script, an UNIX-like environment is required, such as macOS or Linux.
Download and Installation
After downloading, change the file extension to .sh.
How to obtain support
In the tutorial, you can use the option “Provide Feedback”.
License
Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the LICENSE file.
This static website is a program that lets our Scouts group create Elections to determine which leaders will be in our subgroups.
As we don’t know how many kids will be there on Election day (absences…), this program allows for configurations on setup and when ready, shows the voting page with all the candidates entered on setup with a customizable number of votes. That voting page repeats until all the voters have went through.
Since we don’t want to know who voted for who, there is no need to “register”, but we need to confirm if the kid really voted or not, and prevent him from voting more than once (which may skew the results).
Therefore, after a voter’s vote, an overlay shows on the voting page, preventing any further actions until the special input is pressed, which will reset the page to allow for the next voter to actually vote.
There is two way to trigger that special input :
When using a keyboard, you can use the current default keybinding of space
When having a touchscreen, you also have the ability to long press the top half portion of the overlay
This input is only available in the overlay that appears when someone voted, and can therefore not be triggered in the voting page
Whenever that special input is used, a toast notification shows up for a brief moment showing how much voters remains, to keep track of the progress of the current voting session.
That toast notification also has a button to skip remaining voters, in case you miscalculated the number of voters or simply want to skip the remaining ones.
If skipped, the database will contain this information, which means that when loading this database the results will be shown right away (after entering the password, of course).
There is also a way to spread the election between multiple devices to make that election go faster (multiple kids at the same time) : to learn more about this functionality, please scroll down to the relevant section.
Setup page
In the setup page, validations are used to prevent any bad inputs on setup (empty number of voters, empty candidate name, number of votes higher than the number of candidates (which doesn’t make sense, as every candidates would be voted for…), etc).
The number of votes is setup to be a minimum and a maximum : you can give the voters between x and y votes, which allows them for example to vote between 3 to 5 candidates, or even between 0 and 5 candidates, which would mean they could skip their votes if desired.
If there is the need to force a number of votes (for example, the voters need to vote 4 times), simply set the minimum and the maximum to the same number, and this case will be fulfilled.
There is also a configuration option that allows the voters to vote multiple times for the same candidate : simply toggle it on or off (off by default).
This toggle does not affect the number of votes logic described above.
The name of the database is the name that will be given to the file if you want to download the database at the end.
Validations rules on it prevents you from entering a filename that wouldn’t be accepted by Windows systems.
Voting
After setup, at any point, if the page gets reloaded or closed, the database will be downloaded in an unfinished state, allowing you to return to the state you were in.
This allows some sort of protection in case a smart kid decides he wants to press Alt+F4 to mess up stuff.
In the case you want to disable that, there is a checkbox in the setup page that is checked by default – simply uncheck it and the database won’t download automatically.
Even with the automatic download disabled, you will still be able to download the database manually in the end if desired.
As how does the voting process goes, a page gets shown before the actual votes to give a quick reminder to the scout leaders on how the voting process goes (as described below).
The way it works is that all the candidates are shown with an input that is only available to the mouse (or touch) input which is configured based on the informations given at the setup page :
If multiple votes is allowed per candidates, then an input that lets multiple votes on one candidate is used (with a plus and a minus button).
Otherwise, a simple button is shown under each candidate that gives the possibility of voting for that candidate.
Under the candidates cards stack rests a button to submit the votes : it is only enabled once the minimum number of votes (defined at setup!) was reached.
This button also giggles a bit (when enabled) to let the kids know this button is clickable, to (hopefully) remind them that they need to press it.
Above this buttton is a small text line showing how many votes are remaining for the current voter (or two lines if the minimum and the maximum are not the same).
When the minimum number of votes was reached and the voter presses the submit button, an overlay shows up saying “Votes saved, thanks!”, which is a signal that the voter can now leave and do something else.
Nothing on this page is clickable, so using the mouse is now useless in this overlay.
This is where the scout leader comes into play : to go to the next voter, you press the space button on the keyboard and everything is reset in a state that lets another voter give its votes.
On a device with a touch screen (i.e. : a phone), you can press the top half of the screen for a little time (about half a second should do it) and the same behavior will happen.
Once no more voters is remaining, the results flow will kick in – you can follow the next section to learn more about it.
Results
Before showing results, a password is asked to prevent kids from viewing the results by accident.
The password can be set in the setup page, and is optional : if you don’t set any password, when the last voter has finished voting, the overlay will show and going to the next voter will simply show the results directly.
Please note that the password is not encrypted and is simply a bridge between the voters and the scout leaders.
If you used this application before you could set the password (you probably didn’t 😉 ), and you try to load the database, the password still defaults to VL so you don’t get too lost.
The results shown at the end are sorted by number of votes, meaning the most voted candidate will be on top and the lowest voted candidate will be at the bottom.
The rows are clickable, which highlights the candidates (in a cycle of unselected, pre-selected and selected), allowing for discussions with other maintainers.
The button to return to the homepage currently simply reloads the page, as this was an easier way of resetting the program’s state at the time. This behavior is something I want to change, which you can follow my progress in #43.
Sharing elections between multiple devices
The elections can be shared between multiple devices to allow for a single election to be spread across multiple devices.
This is especially useful when there is alot of voters and the time to make everyone vote may be limited, since you can make multiple voters input their votes at the same time.
To use this functionality, you simply need one device that creates the setup for the election, which would then use the dedicated Créer comme Élection Partagée button.
A checkmark is shown next to this button to confirm if you have access to my server that makes this functionality possible.
For more information about this server, please consult this page that explains why it is necessary.
When the shared election is created, a code will be generated and shown on the creator’s screen, which you can now use on other devices to join the created election : you simply need to use the button to join this election on the home page, input the code that was shown on the creator’s screen (share it between yourselves however you want), and all the data will be fetched and the election will start as usual.
Be aware that when voting, requests will be sent to the server to keep it up to date / in synchronization with other devices, so that you don’t vote 20 times on each device if you had 20 voters to begin with.
When doing a request, a loading indicator will be shown to indicate that a request is occurring : the only exception for this is when a voter submits their vote(s), as they might be confused as to why there was something that appeared on screen for no apparent reason.
The functionality to skip the remaining voters allows you to skip the election for everyone, not only the device that it was inputted on.
A warning is shown when using this functionality in the shared election context, to let you truly decide the flow of your own shared election.
If you skip the shared election, no new voters will be available to input their vote(s) : however, devices that already had a voter on it will still be able to send their vote(s).
When no more “seats” are available (when every voter has voted or there are voter that are currenty voting), a page will be shown on devices that have no voters left that lets you know when there is no more voters remaining : it will verify automatically every 30 seconds, or you can manually verify yourself by pressing the button dedicated for it.
This page allows you to bypass the remaining voters if that’s what you need (in case of issues, such has loss of Internet connectivity, for example) – this option however does not delete or skips the election, it simply shows the data that your device currently has.
This page also allows you to send a request to delete the data on the server once the election is over.
Finally, after 24 hours of inactivity (no one joins the shared election, send a vote request, fetches the results, etc), the data will be automatically deleted from my server.
During the whole proceess, errors are handled and if you lose Internet connection, you will, most of the time, have the option to fallback to a local election instead.
This option is there mostly to prevent your data from being lost, so that you can handle problems more easily.
These errors are explained when they happen, and their text will be red to signal that it was an error.
Miscellaneous
All the text in here is in French, since our group is French-based.
Code is in English because code should always be in English.
Delizious Ini is an easy to use .NET Standard library entirely written in C# for reading and writing of INI data
that comes with an intuitive API design applying Domain-driven design (DDD).
It provides extensive configurability and allows to specify failure behaviors (e.g. throw a specific exception in case a section or property does not exist, or proceed with a fallback behavior)
for almost every operation on both instance and operation level.
New features in version 1.21.0
Improve documentation of configurability summary in README
// This configuration represents the loose configuration which is also predefined://var looseConfiguration = IniDocumentConfiguration.Loose;varlooseConfiguration=IniDocumentConfiguration.Default.WithCaseSensitivity(CaseSensitivity.CaseInsensitive)// Treat section names and property keys as case-insensitive.WithNewlineString(NewlineString.Environment)// Use newline string as given by current environment.WithSectionBeginningDelimiter(SectionBeginningDelimiter.Default)// Use default section beginning delimiter which is opening square bracket '['.WithSectionEndDelimiter(SectionEndDelimiter.Default)// Use default section end delimiter which is closing square bracket ']'.WithSectionNameRegex(SectionNameRegex.Default)// Use default section name regex which is '[\p{L}\p{M}\p{N}\p{P}\p{S}\p{Zs}]+'.WithDuplicatePropertyBehavior(DuplicatePropertyBehavior.Ignore)// Ignore subsequent occurrences of a duplicate property by using the first occurrence of such a property.WithDuplicateSectionBehavior(DuplicateSectionBehavior.Merge)// Merge a duplicate section.WithInvalidLineBehavior(InvalidLineBehavior.Ignore)// Ignore when a line is invalid and cannot be parsed on loading.WithPropertyAssignmentSeparator(PropertyAssignmentSeparator.Default)// Use default property assignment separator which is equality sign '='.WithPropertyAssignmentSpacer(PropertyAssignmentSpacer.None)// Use no property assignment spacer.WithPropertyEnumerationMode(PropertyEnumerationMode.Fallback)// Fallback to empty collection of property keys when section does not exist.WithPropertyReadMode(PropertyReadMode.Fallback)// Fallback to empty string when property to read does not exist.WithPropertyWriteMode(PropertyWriteMode.Create)// Create a new property or update an existing property.WithPropertyDeletionMode(PropertyDeletionMode.Ignore)// Ignore when property to delete does not exist.WithSectionDeletionMode(SectionDeletionMode.Ignore)// Ignore when section to delete does not exist.WithCommentString(CommentString.Default)// Use default comment string that indicates the beginning of a comment line which is a semicolon ';'.WithCommentReadMode(CommentReadMode.Fallback)// Fallback to none comment when section or property to read comment does not exist.WithCommentWriteMode(CommentWriteMode.Ignore);// Ignore when section or property to write the comment does not exist// This configuration represents the strict configuration which is also predefined://var strictConfiguration = IniDocumentConfiguration.Strict;varstrictConfiguration=IniDocumentConfiguration.Default.WithCaseSensitivity(CaseSensitivity.CaseInsensitive)// Treat section names and property keys as case-insensitive.WithNewlineString(NewlineString.Environment)// Use newline string as given by current environment.WithSectionBeginningDelimiter(SectionBeginningDelimiter.Default)// Use default section beginning delimiter which is opening square bracket '['.WithSectionEndDelimiter(SectionEndDelimiter.Default)// Use default section end delimiter which is closing square bracket ']'.WithSectionNameRegex(SectionNameRegex.Default)// Use default section name regex which is '[\p{L}\p{M}\p{N}\p{P}\p{S}\p{Zs}]+'.WithDuplicatePropertyBehavior(DuplicatePropertyBehavior.Fail)// Throw exception when a duplicate property occurs.WithDuplicateSectionBehavior(DuplicateSectionBehavior.Fail)// Throw exception when a duplicate section occurs.WithInvalidLineBehavior(InvalidLineBehavior.Fail)// Throw exception when a line is invalid and cannot be parsed on loading.WithPropertyAssignmentSeparator(PropertyAssignmentSeparator.Default)// Use default property assignment separator which is equality sign '='.WithPropertyAssignmentSpacer(PropertyAssignmentSpacer.None)// Use no property assignment spacer.WithPropertyEnumerationMode(PropertyEnumerationMode.Fail)// Throw exception when section to enumerate properties does not exist.WithPropertyReadMode(PropertyReadMode.Fail)// Throw exception when property to read to does not exist.WithPropertyWriteMode(PropertyWriteMode.Update)// Update existing property only but throw exception when property to write does not exist.WithPropertyDeletionMode(PropertyDeletionMode.Fail)// Throw exception when property to delete does not exist.WithSectionDeletionMode(SectionDeletionMode.Fail)// Throw exception when section to delete does not exist.WithCommentString(CommentString.Default)// Use default comment string that indicates the beginning of a comment line which is a semicolon ';'.WithCommentReadMode(CommentReadMode.Fail)// Throw exception when section or property to read comment does not exist.WithCommentWriteMode(CommentWriteMode.Fail);// Throw exception when section or property to write the comment does not exist
conststringini=""" [Section] Property=Current value """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);// Save entire INI document to text writer by using Console.Out to output contentvartextWriter=Console.Out;iniDocument.SaveTo(textWriter);
conststringini=""" [Section] Property=Current value AnotherProperty=Another value EmptyProperty= """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);foreach(varpropertyNameininiDocument.EnumerateProperties("Section")){Console.WriteLine(propertyName);}
The enumeration of properties supports the following modes:
Mode
Description
PropertyEnumerationMode.Fail
Throw a SectionNotFoundException when the section does not exist.
PropertyEnumerationMode.Fallback
Fall back to an empty collection of properties when the section does not exist.
The reading of a property supports the following modes:
Mode
Description
PropertyReadMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
PropertyReadMode.Fallback
Fall back to PropertyValue.None if the section or property does not exist.
PropertyReadMode.CustomFallback
Fall back to the given custom property value if the section or property does not exist.
The writing of a property supports the following modes:
Mode
Description
PropertyWriteMode.Create
Create a new property. If the property already exists, it will be overwritten. If the section does not exist, a new section is created. If the section exists but the property itself does not exist, a new property is created.
PropertyWriteMode.Update
Update an existing property and require that both the section and property exist. Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
conststringini=""" [Section] Property=Current value [EmptySection] [AnotherSection] AnotherProperty=With another value """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.DeleteSection("EmptySection");iniDocument.SaveTo(Console.Out);
The deletion of a section supports the following modes:
Mode
Description
SectionDeletionMode.Fail
Throw a SectionNotFoundException when the section does not exist.
conststringini=""" [Section] Property=Current value AnotherProperty=Another value EmptyProperty= """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.DeleteProperty("Section","Property");iniDocument.SaveTo(Console.Out);
The deletion of a property supports the following modes:
Mode
Description
PropertyDeletionMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
PropertyDeletionMode.Ignore
Silently ignore if the section or the property does not exist.
conststringini=""" [Section] ;This is a sample ;multiline ;comment. :) Property=Value """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);varcomment=iniDocument.ReadComment("Section","Property");Console.WriteLine(comment);
Reading the comment of a property supports the following modes:
Mode
Description
CommentReadMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
CommentReadMode.Fallback
Fall back to none comment if the section or property does not exist.
CommentReadMode.CustomFallback
Fall back to a custom fallback comment if the section or property does not exist.
conststringini=""" [Section] Property=Value """;conststringcomment=""" This is a sample multiline comment. :) """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.WriteComment("Section",comment);usingvartextWriter=newStringWriter();iniDocument.SaveTo(textWriter);textWriter.Flush();Console.WriteLine(textWriter);
Writing the comment of a section supports the following modes:
Mode
Description
CommentWriteMode.Fail
Throw a SectionNotFoundException when the section does not exist.
conststringini=""" [Section] Property=Value """;conststringcomment=""" This is a sample multiline comment. :) """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.WriteComment("Section","Property",comment);usingvartextWriter=newStringWriter();iniDocument.SaveTo(textWriter);textWriter.Flush();Console.WriteLine(textWriter);
Writing the comment of a property supports the following modes:
Mode
Description
CommentWriteMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
CommentWriteMode.Ignore
Silently ignore if the section or the property does not exist.
Delizious Ini is an easy to use .NET Standard library entirely written in C# for reading and writing of INI data
that comes with an intuitive API design applying Domain-driven design (DDD).
It provides extensive configurability and allows to specify failure behaviors (e.g. throw a specific exception in case a section or property does not exist, or proceed with a fallback behavior)
for almost every operation on both instance and operation level.
New features in version 1.21.0
Improve documentation of configurability summary in README
// This configuration represents the loose configuration which is also predefined://var looseConfiguration = IniDocumentConfiguration.Loose;varlooseConfiguration=IniDocumentConfiguration.Default.WithCaseSensitivity(CaseSensitivity.CaseInsensitive)// Treat section names and property keys as case-insensitive.WithNewlineString(NewlineString.Environment)// Use newline string as given by current environment.WithSectionBeginningDelimiter(SectionBeginningDelimiter.Default)// Use default section beginning delimiter which is opening square bracket '['.WithSectionEndDelimiter(SectionEndDelimiter.Default)// Use default section end delimiter which is closing square bracket ']'.WithSectionNameRegex(SectionNameRegex.Default)// Use default section name regex which is '[\p{L}\p{M}\p{N}\p{P}\p{S}\p{Zs}]+'.WithDuplicatePropertyBehavior(DuplicatePropertyBehavior.Ignore)// Ignore subsequent occurrences of a duplicate property by using the first occurrence of such a property.WithDuplicateSectionBehavior(DuplicateSectionBehavior.Merge)// Merge a duplicate section.WithInvalidLineBehavior(InvalidLineBehavior.Ignore)// Ignore when a line is invalid and cannot be parsed on loading.WithPropertyAssignmentSeparator(PropertyAssignmentSeparator.Default)// Use default property assignment separator which is equality sign '='.WithPropertyAssignmentSpacer(PropertyAssignmentSpacer.None)// Use no property assignment spacer.WithPropertyEnumerationMode(PropertyEnumerationMode.Fallback)// Fallback to empty collection of property keys when section does not exist.WithPropertyReadMode(PropertyReadMode.Fallback)// Fallback to empty string when property to read does not exist.WithPropertyWriteMode(PropertyWriteMode.Create)// Create a new property or update an existing property.WithPropertyDeletionMode(PropertyDeletionMode.Ignore)// Ignore when property to delete does not exist.WithSectionDeletionMode(SectionDeletionMode.Ignore)// Ignore when section to delete does not exist.WithCommentString(CommentString.Default)// Use default comment string that indicates the beginning of a comment line which is a semicolon ';'.WithCommentReadMode(CommentReadMode.Fallback)// Fallback to none comment when section or property to read comment does not exist.WithCommentWriteMode(CommentWriteMode.Ignore);// Ignore when section or property to write the comment does not exist// This configuration represents the strict configuration which is also predefined://var strictConfiguration = IniDocumentConfiguration.Strict;varstrictConfiguration=IniDocumentConfiguration.Default.WithCaseSensitivity(CaseSensitivity.CaseInsensitive)// Treat section names and property keys as case-insensitive.WithNewlineString(NewlineString.Environment)// Use newline string as given by current environment.WithSectionBeginningDelimiter(SectionBeginningDelimiter.Default)// Use default section beginning delimiter which is opening square bracket '['.WithSectionEndDelimiter(SectionEndDelimiter.Default)// Use default section end delimiter which is closing square bracket ']'.WithSectionNameRegex(SectionNameRegex.Default)// Use default section name regex which is '[\p{L}\p{M}\p{N}\p{P}\p{S}\p{Zs}]+'.WithDuplicatePropertyBehavior(DuplicatePropertyBehavior.Fail)// Throw exception when a duplicate property occurs.WithDuplicateSectionBehavior(DuplicateSectionBehavior.Fail)// Throw exception when a duplicate section occurs.WithInvalidLineBehavior(InvalidLineBehavior.Fail)// Throw exception when a line is invalid and cannot be parsed on loading.WithPropertyAssignmentSeparator(PropertyAssignmentSeparator.Default)// Use default property assignment separator which is equality sign '='.WithPropertyAssignmentSpacer(PropertyAssignmentSpacer.None)// Use no property assignment spacer.WithPropertyEnumerationMode(PropertyEnumerationMode.Fail)// Throw exception when section to enumerate properties does not exist.WithPropertyReadMode(PropertyReadMode.Fail)// Throw exception when property to read to does not exist.WithPropertyWriteMode(PropertyWriteMode.Update)// Update existing property only but throw exception when property to write does not exist.WithPropertyDeletionMode(PropertyDeletionMode.Fail)// Throw exception when property to delete does not exist.WithSectionDeletionMode(SectionDeletionMode.Fail)// Throw exception when section to delete does not exist.WithCommentString(CommentString.Default)// Use default comment string that indicates the beginning of a comment line which is a semicolon ';'.WithCommentReadMode(CommentReadMode.Fail)// Throw exception when section or property to read comment does not exist.WithCommentWriteMode(CommentWriteMode.Fail);// Throw exception when section or property to write the comment does not exist
conststringini=""" [Section] Property=Current value """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);// Save entire INI document to text writer by using Console.Out to output contentvartextWriter=Console.Out;iniDocument.SaveTo(textWriter);
conststringini=""" [Section] Property=Current value AnotherProperty=Another value EmptyProperty= """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);foreach(varpropertyNameininiDocument.EnumerateProperties("Section")){Console.WriteLine(propertyName);}
The enumeration of properties supports the following modes:
Mode
Description
PropertyEnumerationMode.Fail
Throw a SectionNotFoundException when the section does not exist.
PropertyEnumerationMode.Fallback
Fall back to an empty collection of properties when the section does not exist.
The reading of a property supports the following modes:
Mode
Description
PropertyReadMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
PropertyReadMode.Fallback
Fall back to PropertyValue.None if the section or property does not exist.
PropertyReadMode.CustomFallback
Fall back to the given custom property value if the section or property does not exist.
The writing of a property supports the following modes:
Mode
Description
PropertyWriteMode.Create
Create a new property. If the property already exists, it will be overwritten. If the section does not exist, a new section is created. If the section exists but the property itself does not exist, a new property is created.
PropertyWriteMode.Update
Update an existing property and require that both the section and property exist. Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
conststringini=""" [Section] Property=Current value [EmptySection] [AnotherSection] AnotherProperty=With another value """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.DeleteSection("EmptySection");iniDocument.SaveTo(Console.Out);
The deletion of a section supports the following modes:
Mode
Description
SectionDeletionMode.Fail
Throw a SectionNotFoundException when the section does not exist.
conststringini=""" [Section] Property=Current value AnotherProperty=Another value EmptyProperty= """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.DeleteProperty("Section","Property");iniDocument.SaveTo(Console.Out);
The deletion of a property supports the following modes:
Mode
Description
PropertyDeletionMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
PropertyDeletionMode.Ignore
Silently ignore if the section or the property does not exist.
conststringini=""" [Section] ;This is a sample ;multiline ;comment. :) Property=Value """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);varcomment=iniDocument.ReadComment("Section","Property");Console.WriteLine(comment);
Reading the comment of a property supports the following modes:
Mode
Description
CommentReadMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
CommentReadMode.Fallback
Fall back to none comment if the section or property does not exist.
CommentReadMode.CustomFallback
Fall back to a custom fallback comment if the section or property does not exist.
conststringini=""" [Section] Property=Value """;conststringcomment=""" This is a sample multiline comment. :) """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.WriteComment("Section",comment);usingvartextWriter=newStringWriter();iniDocument.SaveTo(textWriter);textWriter.Flush();Console.WriteLine(textWriter);
Writing the comment of a section supports the following modes:
Mode
Description
CommentWriteMode.Fail
Throw a SectionNotFoundException when the section does not exist.
conststringini=""" [Section] Property=Value """;conststringcomment=""" This is a sample multiline comment. :) """;usingvartextReader=newStringReader(ini);variniDocument=IniDocument.LoadFrom(textReader,IniDocumentConfiguration.Default);iniDocument.WriteComment("Section","Property",comment);usingvartextWriter=newStringWriter();iniDocument.SaveTo(textWriter);textWriter.Flush();Console.WriteLine(textWriter);
Writing the comment of a property supports the following modes:
Mode
Description
CommentWriteMode.Fail
Throw a SectionNotFoundException when the section does not exist, or throw a PropertyNotFoundException when the section exists but the property does not exist.
CommentWriteMode.Ignore
Silently ignore if the section or the property does not exist.
If we want our code to transpile to es5 for cross-browser suport we need to add babel-core and babel-loader.
Also it is best to add the babel-preset-env to have support for stage 4 proposals.
$ npm i babel-core babel-loader babel-preset-env --save-dev
Install this package and include it into any js file :
constApiTest=require('../../index');// Package come with a Check module that has predefined functions to check common types.constCheck=ApiTest.check;// Send GET message to /hello and check the returned value is a string and strictly 'hello'.ApiTest.get('/hello',(v)=>Check.typ.string(v)&&v==='hello','Get hello message from server','STANDARD API');// You can define custom common functions by overriding Check ones.Check.success=v=>!!v['result']&&v.result==='success';// Send POST message with { name: 'foo_0' } payload and check return according Check.success.ApiTest.post(`/post/user`,{name: 'foo_0'},(v)=>Check.success(v),`Post foo_0 user`,'USER API MANAGEMENT');// Send POST message with a bad payload and check that it does lead to a managed error.Check.error=v=>!!v['result']&&v.result==='failed';ApiTest.post(`/post/user`,{bad_value: 'bad_content'},(v)=>Check.error(v),`Post a bad payload to add user`,'USER API MANAGEMENT');// You have access to 'testFor' function calling the s => { ... } function for all [ 'foo_1', ... ] list elements.ApiTest.testFor(['foo_1','foo_2','foo_3'],s=>{ApiTest.post(`/post/user`,{name: s},(v)=>Check.success(v),`Post ${s} user`,'USER API MANAGEMENT');});// When every test is declared use ApiTest.run() function to run everything.ApiTest.run();
Then run tests using node interpreter
$ node tests/hello/hello.js --help
Usage: hello [options] <url>
Options:
-ss, --selfSigned <bool> Accept Self Signed Certificate
-ucao, --useAuthorityOnly <bool> Use only authority file
-ca, --authority <path> Path to certificate authority file
-cert, --certificate <path> Path to certificate file
-pk, --privateKey <path> Path to the associate private key file
-up12, --usePkcs12 <bool> Use P12 file
-p12, --pkcs12 <path> Path to the associate P12 file
-p12pw, --pkcs12Password <string> Password associated with the P12 file
-https, --useHttps <bool> Should use HTTPS instead of HTTP
-vr, --verboseResponse <bool> Should print server responses
-vr, --verboseError <bool> Should print server errors responses
-a, --await <[1; 360000] as integer> Set an await time between requests
-m, --matching <string> Execute tests matching given string
-pn, --productName <string> Execute parametered product named tests
-h, --help display help for command
$ node tests/hello/hello.js http://127.0.0.1:4200/