Skip to content

Commit

Permalink
move application.yaml to collections/windows.yaml #40
Browse files Browse the repository at this point in the history
  • Loading branch information
undergroundwires committed Jan 9, 2021
1 parent 72e925f commit 6b83dcb
Show file tree
Hide file tree
Showing 24 changed files with 265 additions and 286 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
- There are two types of components:
- **Stateless**, extends `Vue`
- **Stateful**, extends [`StatefulVue`](./src/presentation/StatefulVue.ts)
- The source of truth for the state lies in application layer (`./src/application/`) and must be updated from the views if they're mutating the state
- They mutate or/and react to changes in [application state](src/application/Context/State/CategoryCollectionState.ts).
- The source of truth for the state lies in application layer ([`./src/application/`](src/application/)) and must be updated from the views if they're mutating the state
- They mutate or/and react to state changes in [ApplicationContext](src/application/Context/ApplicationContext.ts).
- You can react by getting the state and listening to it and update the view accordingly in [`mounted()`](https://vuejs.org/v2/api/#mounted) method.

## License
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@

## Extend scripts

- Fork it & add more scripts in [application.yaml](src/application/application.yaml) and send a pull request 👌
- 📖 If you're unsure about the syntax you can refer to the [application file | documentation](docs/application-file.md).
- 🙏 For any new script, please add `revertCode` and `docs` values if possible.
1. Fork the repository
2. Add more scripts in respective script collection in [collections](src/application/collections/) folder.
- 📖 If you're unsure about the syntax you can refer to the [collection files | documentation](docs/collection-files.md).
- 🙏 For any new script, please add `revertCode` and `docs` values if possible.
3. Send a pull request 👌

## Commands

Expand Down Expand Up @@ -69,7 +71,9 @@
- [ApplicationContext](src/application/Context/ApplicationContext.ts)
- Holds the [CategoryCollectionState](src/application/Context/State/CategoryCollectionState.ts)] for each OS
- Same instance is shared throughout the application
- The application is defined & controlled in a [single YAML file](src/application/application.yaml) using[data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming)
- The scripts are defined and controlled in [yaml files](src/application/collections/) per OS
- Uses [data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming)
- 📖 See [extend scripts](#extend-scripts) to read about how to extend them.

![DDD + vue.js](img/architecture/app-ddd.png)

Expand Down
34 changes: 17 additions & 17 deletions docs/application-file.md → docs/collection-files.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
# Application file
# Collection files

- privacy.sexy is a data-driven application where it reads the necessary OS-specific logic from [`application.yaml`](./../src/application/application.yaml)
- privacy.sexy is a data-driven application where it reads the necessary OS-specific logic from yaml files in [`application/collections`](./../src/application/collections/)
- 💡 Best practices
- If you repeat yourself, try to utilize [YAML-defined functions](#function)
- Always try to add documentation and a way to revert a tweak in [scripts](#script)
- 📖 Types in code: [`application.d.ts`](./../src/application/application.yaml.d.ts)
- If you repeat yourself, try to utilize [YAML-defined functions](#Function)
- Always try to add documentation and a way to revert a tweak in [scripts](#Script)
- 📖 Types in code: [`collection.yaml.d.ts`](./../src/application/collections/collection.yaml.d.ts)

## Objects

### `Application`
### `Collection`

- Application file simply defines:
- A collection simply defines:
- different categories and their scripts in a tree structure
- OS specific details
- Application file also allows defining common [function](#function)s to be used throughout the application if you'd like different scripts to share same code.
- Also allows defining common [function](#Function)s to be used throughout the collection if you'd like different scripts to share same code.

#### `Application` syntax
#### `Collection` syntax

- `os:` *`string`* (**required**)
- Operating system that the application file is written for.
- Operating system that the [Collection](#collection) is written for.
- 📖 See [OperatingSystem.ts](./../src/domain/OperatingSystem.ts) enumeration for allowed values.
- `actions: [` ***[`Category`](#Category)*** `, ... ]` **(required)**
- Each [category](#category) is rendered as different cards in card presentation.
-Application must consist of at least one category.
-A [Collection](#collection) must consist of at least one category.
- `functions: [` ***[`Function`](#Function)*** `, ... ]`
- Functions are optionally defined to re-use the same code throughout different scripts.
- `scripting:` ***[`ScriptingDefinition`](#ScriptingDefinition)*** **(required)**
Expand All @@ -37,8 +37,8 @@

- `category:` *`string`* (**required**)
- Name of the category
- ❗ Must be unique throughout the application
- `children: [` ***[`Category`](#category)*** `|` [***`Script`***](#Script) `, ... ]` (**required**)
- ❗ Must be unique throughout the [Collection](#collection)
- `children: [` ***[`Category`](#Category)*** `|` [***`Script`***](#Script) `, ... ]` (**required**)
- ❗ Category must consist of at least one subcategory or script.
- Children can be combination of scripts and subcategories.

Expand All @@ -54,7 +54,7 @@

- `name`: *`string`* (**required**)
- Name of the script
- ❗ Must be unique throughout the application
- ❗ Must be unique throughout the [Collection](#collection)
- E.g. `Disable targeted ads`
- `code`: *`string`* (may be **required**)
- Batch file commands that will be executed
Expand Down Expand Up @@ -86,7 +86,7 @@

- `function`: *`string`* (**required**)
- Name of the function to call.
- ❗ Function with same name must defined in `functions` property of [Application](#application)
- ❗ Function with same name must defined in `functions` property of [Collection](#collection)
- `parameters`: `[ parameterName:` *`parameterValue`*`, ... ]`
- Defines key value dictionary for each parameter and its value
- E.g.
Expand Down Expand Up @@ -134,7 +134,7 @@ It would print "Hello world" if it's called in a [script](#script) as following:
- ❗ Function names must be unique
- `parameters`: `[` *`string`* `, ... ]`
- Name of the parameters that the function has.
- Parameter values are provided by a [Script](#script) through a [FunctionCall](#functioncall)
- Parameter values are provided by a [Script](#script) through a [FunctionCall](#FunctionCall)
- Parameter names must be defined to be used in expressions such as [parameter substitution](#parameter-substitution)
- ❗ Parameter names must be unique
`code`: *`string`* (**required**)
Expand All @@ -147,7 +147,7 @@ It would print "Hello world" if it's called in a [script](#script) as following:

### `ScriptingDefinition`

- Defines global properties for scripting that's used throughout the application file.
- Defines global properties for scripting that's used throughout its parent [Collection](#collection).

#### `ScriptingDefinition` syntax

Expand Down
12 changes: 6 additions & 6 deletions src/application/Parser/ApplicationParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@ import { IApplication } from '@/domain/IApplication';
import { IProjectInformation } from '@/domain/IProjectInformation';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { parseCategoryCollection } from './CategoryCollectionParser';
import applicationFile, { YamlApplication } from 'js-yaml-loader!@/application/application.yaml';
import WindowsData from 'js-yaml-loader!@/application/collections/windows.yaml';
import { CollectionData } from 'js-yaml-loader!@/*';
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
import { Application } from '@/domain/Application';

export function parseApplication(
parser = CategoryCollectionParser,
processEnv: NodeJS.ProcessEnv = process.env,
collectionData = CollectionData): IApplication {
collectionData = LoadedCollectionData): IApplication {
const information = parseProjectInformation(processEnv);
const collection = parser(collectionData, information);
const app = new Application(information, [ collection ]);
return app;
}

export type CategoryCollectionParserType
= (file: YamlApplication, info: IProjectInformation) => ICategoryCollection;
= (file: CollectionData, info: IProjectInformation) => ICategoryCollection;

const CategoryCollectionParser: CategoryCollectionParserType
= (file, info) => parseCategoryCollection(file, info);

const CollectionData: YamlApplication
= applicationFile;

const LoadedCollectionData: CollectionData
= WindowsData;
6 changes: 3 additions & 3 deletions src/application/Parser/CategoryCollectionParser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Category } from '@/domain/Category';
import { YamlApplication } from 'js-yaml-loader!@/application.yaml';
import { CollectionData } from 'js-yaml-loader!@/*';
import { parseCategory } from './CategoryParser';
import { ScriptCompiler } from './Compiler/ScriptCompiler';
import { OperatingSystem } from '@/domain/OperatingSystem';
Expand All @@ -10,7 +10,7 @@ import { CategoryCollection } from '@/domain/CategoryCollection';
import { IProjectInformation } from '@/domain/IProjectInformation';

export function parseCategoryCollection(
content: YamlApplication,
content: CollectionData,
info: IProjectInformation,
osParser = createEnumParser(OperatingSystem)): ICategoryCollection {
validate(content);
Expand All @@ -29,7 +29,7 @@ export function parseCategoryCollection(
return collection;
}

function validate(content: YamlApplication): void {
function validate(content: CollectionData): void {
if (!content) {
throw new Error('content is null or undefined');
}
Expand Down
36 changes: 18 additions & 18 deletions src/application/Parser/CategoryParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { YamlCategory, YamlScript } from 'js-yaml-loader!@/application.yaml';
import { CategoryData, ScriptData, CategoryOrScriptData } from 'js-yaml-loader!@/*';
import { Script } from '@/domain/Script';
import { Category } from '@/domain/Category';
import { parseDocUrls } from './DocumentationParser';
Expand All @@ -12,7 +12,7 @@ interface ICategoryChildren {
subScripts: Script[];
}

export function parseCategory(category: YamlCategory, compiler: IScriptCompiler): Category {
export function parseCategory(category: CategoryData, compiler: IScriptCompiler): Category {
if (!compiler) {
throw new Error('undefined compiler');
}
Expand All @@ -21,8 +21,8 @@ export function parseCategory(category: YamlCategory, compiler: IScriptCompiler)
subCategories: new Array<Category>(),
subScripts: new Array<Script>(),
};
for (const categoryOrScript of category.children) {
parseCategoryChild(categoryOrScript, children, category, compiler);
for (const data of category.children) {
parseCategoryChild(data, children, category, compiler);
}
return new Category(
/*id*/ categoryIdCounter++,
Expand All @@ -33,7 +33,7 @@ export function parseCategory(category: YamlCategory, compiler: IScriptCompiler)
);
}

function ensureValid(category: YamlCategory) {
function ensureValid(category: CategoryData) {
if (!category) {
throw Error('category is null or undefined');
}
Expand All @@ -46,28 +46,28 @@ function ensureValid(category: YamlCategory) {
}

function parseCategoryChild(
categoryOrScript: any,
data: CategoryOrScriptData,
children: ICategoryChildren,
parent: YamlCategory,
parent: CategoryData,
compiler: IScriptCompiler) {
if (isCategory(categoryOrScript)) {
const subCategory = parseCategory(categoryOrScript as YamlCategory, compiler);
if (isCategory(data)) {
const subCategory = parseCategory(data as CategoryData, compiler);
children.subCategories.push(subCategory);
} else if (isScript(categoryOrScript)) {
const yamlScript = categoryOrScript as YamlScript;
const script = parseScript(yamlScript, compiler);
} else if (isScript(data)) {
const scriptData = data as ScriptData;
const script = parseScript(scriptData, compiler);
children.subScripts.push(script);
} else {
throw new Error(`Child element is neither a category or a script.
Parent: ${parent.category}, element: ${JSON.stringify(categoryOrScript)}`);
Parent: ${parent.category}, element: ${JSON.stringify(data)}`);
}
}

function isScript(categoryOrScript: any): boolean {
return (categoryOrScript.code && categoryOrScript.code.length > 0)
|| categoryOrScript.call;
function isScript(data: any): boolean {
return (data.code && data.code.length > 0)
|| data.call;
}

function isCategory(categoryOrScript: any): boolean {
return categoryOrScript.category && categoryOrScript.category.length > 0;
function isCategory(data: any): boolean {
return data.category && data.category.length > 0;
}
6 changes: 3 additions & 3 deletions src/application/Parser/Compiler/IScriptCompiler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IScriptCode } from '@/domain/IScriptCode';
import { YamlScript } from 'js-yaml-loader!@/application.yaml';
import { ScriptData } from 'js-yaml-loader!@/*';

export interface IScriptCompiler {
canCompile(script: YamlScript): boolean;
compile(script: YamlScript): IScriptCode;
canCompile(script: ScriptData): boolean;
compile(script: ScriptData): IScriptCode;
}
34 changes: 17 additions & 17 deletions src/application/Parser/Compiler/ScriptCompiler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { generateIlCode, IILCode } from './ILCode';
import { IScriptCode } from '@/domain/IScriptCode';
import { ScriptCode } from '@/domain/ScriptCode';
import { YamlScript, YamlFunction, FunctionCall, ScriptFunctionCall, FunctionCallParameters } from 'js-yaml-loader!@/application.yaml';
import { ScriptData, FunctionData, FunctionCallData, ScriptFunctionCallData, FunctionCallParametersData } from 'js-yaml-loader!@/*';
import { IScriptCompiler } from './IScriptCompiler';

interface ICompiledCode {
Expand All @@ -10,16 +10,16 @@ interface ICompiledCode {
}

export class ScriptCompiler implements IScriptCompiler {
constructor(private readonly functions: readonly YamlFunction[]) {
constructor(private readonly functions: readonly FunctionData[]) {
ensureValidFunctions(functions);
}
public canCompile(script: YamlScript): boolean {
public canCompile(script: ScriptData): boolean {
if (!script.call) {
return false;
}
return true;
}
public compile(script: YamlScript): IScriptCode {
public compile(script: ScriptData): IScriptCode {
this.ensureCompilable(script.call);
const compiledCodes = new Array<ICompiledCode>();
const calls = getCallSequence(script.call);
Expand All @@ -36,15 +36,15 @@ export class ScriptCompiler implements IScriptCompiler {
return new ScriptCode(script.name, scriptCode.code, scriptCode.revertCode);
}

private getFunctionByName(name: string): YamlFunction {
private getFunctionByName(name: string): FunctionData {
const func = this.functions.find((f) => f.name === name);
if (!func) {
throw new Error(`called function is not defined "${name}"`);
}
return func;
}

private ensureCompilable(call: ScriptFunctionCall) {
private ensureCompilable(call: ScriptFunctionCallData) {
if (!this.functions || this.functions.length === 0) {
throw new Error('cannot compile without shared functions');
}
Expand All @@ -62,15 +62,15 @@ function printList(list: readonly string[]): string {
return `"${list.join('","')}"`;
}

function ensureNoDuplicatesInFunctionNames(functions: readonly YamlFunction[]) {
function ensureNoDuplicatesInFunctionNames(functions: readonly FunctionData[]) {
const duplicateFunctionNames = getDuplicates(functions
.map((func) => func.name.toLowerCase()));
if (duplicateFunctionNames.length) {
throw new Error(`duplicate function name: ${printList(duplicateFunctionNames)}`);
}
}

function ensureNoDuplicatesInParameterNames(functions: readonly YamlFunction[]) {
function ensureNoDuplicatesInParameterNames(functions: readonly FunctionData[]) {
const functionsWithParameters = functions
.filter((func) => func.parameters && func.parameters.length > 0);
for (const func of functionsWithParameters) {
Expand All @@ -81,7 +81,7 @@ function ensureNoDuplicatesInParameterNames(functions: readonly YamlFunction[])
}
}

function ensureNoDuplicateCode(functions: readonly YamlFunction[]) {
function ensureNoDuplicateCode(functions: readonly FunctionData[]) {
const duplicateCodes = getDuplicates(functions.map((func) => func.code));
if (duplicateCodes.length > 0) {
throw new Error(`duplicate "code" in functions: ${printList(duplicateCodes)}`);
Expand All @@ -94,7 +94,7 @@ function ensureNoDuplicateCode(functions: readonly YamlFunction[]) {
}
}

function ensureValidFunctions(functions: readonly YamlFunction[]) {
function ensureValidFunctions(functions: readonly FunctionData[]) {
if (!functions) {
return;
}
Expand All @@ -118,20 +118,20 @@ function merge(codes: readonly ICompiledCode[]): ICompiledCode {
};
}

function compileCode(func: YamlFunction, parameters: FunctionCallParameters): ICompiledCode {
function compileCode(func: FunctionData, parameters: FunctionCallParametersData): ICompiledCode {
return {
code: compileExpressions(func.code, parameters),
revertCode: compileExpressions(func.revertCode, parameters),
};
}

function compileExpressions(code: string, parameters: FunctionCallParameters): string {
function compileExpressions(code: string, parameters: FunctionCallParametersData): string {
let intermediateCode = generateIlCode(code);
intermediateCode = substituteParameters(intermediateCode, parameters);
return intermediateCode.compile();
}

function substituteParameters(intermediateCode: IILCode, parameters: FunctionCallParameters): IILCode {
function substituteParameters(intermediateCode: IILCode, parameters: FunctionCallParametersData): IILCode {
const parameterNames = intermediateCode.getUniqueParameterNames();
if (parameterNames.length && !parameters) {
throw new Error(`no parameters defined, expected: ${printList(parameterNames)}`);
Expand All @@ -146,7 +146,7 @@ function substituteParameters(intermediateCode: IILCode, parameters: FunctionCal
return intermediateCode;
}

function ensureValidCall(call: FunctionCall, scriptName: string) {
function ensureValidCall(call: FunctionCallData, scriptName: string) {
if (!call) {
throw new Error(`undefined function call in script "${scriptName}"`);
}
Expand All @@ -155,9 +155,9 @@ function ensureValidCall(call: FunctionCall, scriptName: string) {
}
}

function getCallSequence(call: ScriptFunctionCall): FunctionCall[] {
function getCallSequence(call: ScriptFunctionCallData): FunctionCallData[] {
if (call instanceof Array) {
return call as FunctionCall[];
return call as FunctionCallData[];
}
return [ call as FunctionCall ];
return [ call as FunctionCallData ];
}
Loading

0 comments on commit 6b83dcb

Please sign in to comment.