Code Monkey home page Code Monkey logo

heleonix.reflection's Introduction

Heleonix

Flexible declarative framework for web sites and web applications.

Install

TODO

IMPLEMENT

- @heleonix/testing wrapped mocks, auto deep mocks, auto shallow mocks, manual mocks
    ○ @heleonix/testing-jasmine
    ○ @heleonix/testing-jest
- Store full path to each view being rendered in the presenter and pass it to an exception if any
- Review all implementation and implement validation functions which throw exceptions for all components and all cases
- Use Object.is to compare values in setters of data, property, state decorators

CONTEXT

Context is an object with string values, which splits resources of applications:

{
    "env": "dev",
    "customer": "customer1",
    "culture": "uk-UK"
}

In webpack plugins context is specified as below:

[
    // Least specific
    {
        "name": "env",
        "values": ["dev", "test", "prod"],
    },
    {
        "name": "customer",
        "values": ["customer1", "customer2", "customer3"],
    },
    {
        "name": "culture",
        "values": ["en-US", "uk-UK"],
    },
    // Most specific
];

Context-specific files are named in format:

MyFile.ext MyFile.uk-UK.customer1.dev.ext MyFile.uk-UK.ext MyFile.dev.ext MyFile.customer1.ext

DICTIONARIES (MERGEABLE)

Buttons.en-US.dic Buttons.en-US.customer1.dic Buttons.es-ES.customer1.dic

Buttons.test.customer2.en-US.dic:

<Dictionary>
    <Add>Add</Add>
    <Remove>Remove</Remove>
    <Ok>OK {usertitle}</Ok>
    <!--for this message you must provide params: username, usertitle-->
    <OkOrCancel>
        Hi {username}! Are you {.Ok} or {@BaseControls.Cancel}?
    </OkOrCancel>
</Dictionary>

Compiled into:

{
    "name": "Buttons",
    "context": {
        "env": "test",
        "customer": "customer1",
        "culture": "en-US"
    },
    "items": {
        "Add": "Add",
        "Remove": "Remove",
        "Ok": "OK {usertitle}",
        "OkOrCancel": "Hi {username}! Are you {.Ok} or {#BaseControls.Cancel}?"
    }
}

Have methods:

  • setup(definition), like controls
  • getValue(key, args)

SETTINGS (MERGEABLE)

Key/Value pairs like dictionaries but with values of any JSON type and they cannot have replacement parameters and references to other keys or settings.

STYLES (MERGEABLE, EXTENDABLE)

Style is applied as class="auto generated classes" to the root native html elements only, i.e.:

<div class="auto generated classes">
	<button />
	<button />
	<button />
</div>

OR

<li class="auto generated classes">one</li>
<li class="auto generated classes">two</li>
<li class="auto generated classes">three</li>

MyView.style MyView.customer1.style MyView.en-US.customer1.style

MyView.style:

<Style extends="MyBaseView">
	<border-color value="#aaa" />
  <width>
	<color>
    <!-- Think about conditional setting -->
  </color>
</Style>

Compiled into:

{
	"extends": "MyBaseView",
	"items": {
		"border-color": "#aaa";
		"color": "vm => vm.isValid ? 'green' : 'red'"
	}
}

Style.js:

THEMES

TBD

CONTROLS

<!--CustomAddButton-->
<Control>
    <div>
        <button />
    </div>
    <OnChanging property="extraValueForCustomControls=" name="handleChanging">
        ...
    </OnChanging>
</Control>

<Control>
    <FromToList name="roleSelector"
        isReadonly="settings.UIConfig.isReadonly | converter1"
        from.items="availableItems"
        from.items.Item.template=""
        to.items="selectedItems"
        add.template="dictionaries.MyTemplates.CustomAddButton - for exactly the control with the 'add' name"
        Button.template="dictionaries.MyTemplates.CustomAddButton - for all buttons in this control. If there is a control with name 'Button' and Button control, handle it as an error"
        subControlName.Button.template="dictionaries.MyTemplates.CustomAddButton - for all buttons in the 'subControlName' control"
        add.text="dictionaries.Buttons.add | converter1"
        add.extraValueForCustomControls="extraValue"
    />
    <OnAdding property="roleSelector.from.items" name="doSomethingOnAdding">
        <Update property="roleSelector.prop1" value="roleSelector.add.prop2" />
        <Add />
        <Remove />
        <Move />
        <Raise event="someEvent" />
        <Call task="FetchSomething" prop1="prop1" prop2="prop2" prop3="prop3" />
    </OnAdding>
    <OnRaising event="add.clicked" name="doSomethingOnClicked">
        <!--...-->
    </OnRaising>
</Control>



FromToList.view:

```xml
<Control>
	<div>
		<List name="from" />
		<Button name="add" />
		<Button name="remove" />
		<List name="to" />
	</div>
</Control>

Switch:

TODO

When:

TODO

List:

<div>
	<List name="myUsers" for="users" by="id" extraProperty="extraValue" Item.template="Dictionaries.Templates.CustomListItem" />
</div>

<!--CustomListItem: Dynamic name is [0], [1], [2] etc for getting/setting properties via myUsers.[0].id-->
<Control>
    <button name="id" value="extraProperty">item.id</button>
</Control>

SERVICES

Can inject another services.

HttpService - provides many scenarios with requests:

  • sequential requests
  • parallel requests
  • polling with intervals and specified number of retries
  • optimistic updates with pending statuses
  • etc
  • Returns an empty object with internal promise, so it can be bound to a view, so when promise is resolved, the empty object is populated with data and model is updated and view is refreshed.

CONVERTERS

classes with "format", "parse", getDictionary(dictionaryName, keyName, ...args), getSetting(...) functions.

TASKS

have "run" function and any number of properties. have getSetting(settingName, settingKey) function. Can inject services.

PROVIDERS

Provide dictionary definitions, control definitions, style definitions, theme definitions, settings definitions. Can inject services. have getSetting(settingName, settingKey) function.

index.html -> <div id="root"></div>

index.js -> new MyApplication().run()

heleonix.reflection's People

Contributors

hennadiilu avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar

heleonix.reflection's Issues

System does not deal with Dictionary object correctly

Firstly really useful library, thanks for putting it together.

I've found an issue when using Dictionary classes. The square bracket annotation in the reflection string returns the value at the index location of the Dictionary rather than using the Key of the dictionary.

Example code that shows the issue can be found here

using Heleonix.Reflection;
using System;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;

namespace Test
{
#pragma warning disable CS1591

    public class TestData
    {
        public static string[] ArrayTest => ["One", "Two"];

        public static Collection<string> CollectionTest = ["One", "Two"];

        public static Dictionary<int, string> IntDictionaryTest = new()
        {
            { 3, "Three" },
            { 4, "Four" }
        };

        public static Dictionary<string, string> StringDictionaryTest = new()
        {
            { "Three", "Three" },
            { "Four", "Four" }
        };

    }

#pragma warning restore CS1591

    class Program
    {
        static void Main()
        {
            TestData testData = new();            

            if (Reflector.Get(testData, null, "ArrayTest[0]", out string arrayValue0)) Console.WriteLine("Array Index 0: " + arrayValue0);
            if (Reflector.Get(testData, null, "ArrayTest[1]", out string arrayValue1)) Console.WriteLine("Array Index 1: " + arrayValue1);

            if (Reflector.Get(testData, null, "CollectionTest[0]", out string collectionValue0)) Console.WriteLine("Collection Index 0: " + collectionValue0);
            if (Reflector.Get(testData, null, "CollectionTest[1]", out string collectionValue1)) Console.WriteLine("Collection Index 1: " + collectionValue1);
            
            if (Reflector.Get(testData, null, "IntDictionaryTest[0]", out string intDictionaryValue0)) Console.WriteLine("Dictionary Int Index 0: " + intDictionaryValue0);
            if (Reflector.Get(testData, null, "IntDictionaryTest[1]", out string intDictionaryValue1)) Console.WriteLine("Dictionary Int Index 1: " + intDictionaryValue1);
            if (Reflector.Get(testData, null, "IntDictionaryTest[2]", out string intDictionaryValue2)) Console.WriteLine("Dictionary Int Index 2: " + intDictionaryValue2);
            if (Reflector.Get(testData, null, "IntDictionaryTest[3]", out string intDictionaryValue3)) Console.WriteLine("Dictionary Int Index 3: " + intDictionaryValue3);
            if (Reflector.Get(testData, null, "IntDictionaryTest[4]", out string intDictionaryValue4)) Console.WriteLine("Dictionary Int Index 4: " + intDictionaryValue4);

            if (Reflector.Get(testData, null, "StringDictionaryTest[Zero]", out string stringDictionaryValue0)) Console.WriteLine("Dictionary String Index 0: " + stringDictionaryValue0);
            if (Reflector.Get(testData, null, "StringDictionaryTest[One]", out string stringDictionaryValue1)) Console.WriteLine("Dictionary String Index 1: " + stringDictionaryValue1);
            if (Reflector.Get(testData, null, "StringDictionaryTest[Two]", out string stringDictionaryValue2)) Console.WriteLine("Dictionary String Index 2: " + stringDictionaryValue2);
            if (Reflector.Get(testData, null, "StringDictionaryTest[Three]", out string stringDictionaryValue3)) Console.WriteLine("Dictionary String Index 3: " + stringDictionaryValue3);
            if (Reflector.Get(testData, null, "StringDictionaryTest[Four]", out string stringDictionaryValue4)) Console.WriteLine("Dictionary String Index 4: " + stringDictionaryValue4);
        }
    }

}

For example, IntDictionaryTest[0] and IntDictionaryTest[1] return the first and second element of the Dictionary rather like they are collections when it should return nothing, IntDictionaryTest[3] and [4] return no values.

To resolve the issue I've made the following changes to

private static object GetElementAt(object container, object index)
        {
            if (container is IDictionary)
            {
                var objectAsDictionary = ((IEnumerable)container).Cast<object>().AsEnumerable().ToDictionary(px => px.GetType().GetProperty("Key").GetValue(px), pv => pv.GetType().GetProperty("Value").GetValue(pv));
                return objectAsDictionary.FirstOrDefault(pair => pair.Key == index || pair.Key.ToString() == index.ToString()).Value;
            }

            if (container is IList list)
            {
                if ((int)index >= list.Count)
                {
                    return null;
                }

                return list[(int)index];
            }

            if (container is IEnumerable enumerable)
            {
                var enumerator = enumerable.GetEnumerator();
                var intIndex = (int)index;

                while (enumerator.MoveNext())
                {
                    if (intIndex == 0)
                    {
                        return enumerator.Current;
                    }

                    intIndex--;
                }
            }

            return null;
        }

and

public static bool Get<TReturn>(
            object instance,
            Type type,
            string memberPath,
            out TReturn value,
            BindingFlags bindingFlags = DefaultBindingFlags)
        {
            if ((instance == null && type == null) || string.IsNullOrEmpty(memberPath))
            {
                value = default;

                return false;
            }

            var container = instance;
            var containerType = container?.GetType() ?? type;

            while (true)
            {
                var dot = memberPath.IndexOf('.');
                var size = (dot == -1) ? memberPath.Length : dot;
                var prop = memberPath.Substring(0, size);

                object index = null;
                var indexerEnd = prop.LastIndexOf(']');

                if (indexerEnd != -1)
                {
                    var indexerStart = prop.IndexOf('[');

                    if (int.TryParse(prop.Substring(indexerStart + 1, indexerEnd - indexerStart - 1), out int result))
                    {
                        index = result;
                    }
                    else
                    {
                        index = prop.Substring(indexerStart + 1, indexerEnd - indexerStart - 1);
                    }

                    prop = prop.Substring(0, indexerStart);
                }

Supplied test program then works as expected.

If you want me to do a pull request let me know.

Index returning null for values that don't exist

Found a second issue when doing more testing that values using the index function can return null when there is no value in the Collection etc at that Index.

I've resolved it in the build and will put a pull request in for you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.