GithubHelp home page GithubHelp logo

constrictor's Introduction

Korey's Holopin Board

@Korey's Holopin board

constrictor's People

Contributors

koreyhinton avatar

Watchers

 avatar  avatar  avatar

constrictor's Issues

Compile-time containers

This could solve issue #1.

Generate shared/singleton containers, that will fail at compile time (as opposed to runtime registration). Actually no registration would be needed, it would follow the pattern of new object constructor initialization, but internally store the singleton that gets shared.

public interface Container<T>
{
    T Get();
}
public class Log
{
    public void Write(string message) => throw new NotImplementedException();
}
public class LogContainer : Container<Log>
{
    private static readonly Log _sharedInstance = new Log();
    public Log Get() => _sharedInstance;
}
public class LogLazyContainer : Container<Log>
{
    private static Log Instance { get { return Nested.Instance; } }
    private class Nested
    {
        static Nested()
        {
        }
        internal static readonly Log Instance = new Log();
    }
    public Log Get() => Instance;
}
public class MyClass
{
    public MyClass(LogContainer logContainer)
    {
        _logContainer = logContainer;
    }
    private readonly LogContainer _logContainer;
    public void DoSomething()
    {
        var log = _logContainer.Get();
        log.Write("doing something");
    }
}
public class MyOtherClass
{
    public MyOtherClass(LogContainer logContainer)
    {
        _log = logContainer.Get();//container goes out of scope
    }
    private readonly Log _log;
}

...

var app = new App(new LogLazyContainer(), new MyClass(new LogContainer()), new MyOtherClass(new LogContainer()));
app.Run();

The generated code should be scriptable and positional

The concept of a "Main Interface" should be removed. It is too coupled to the idea of a main program file being generated and the core purpose of this framework is to generate the code, and if it tries to dictate the program structure, then it takes away from that purpose.

This will allow being able to separate out the "construction" from the "constructors", and even make it part of the build process, ie:

./my_cs_constrictor --construction > src/Construction.cs
./my_cs_constrictor --constructors > src/Constructors.cs

Similarly, if DI was being generated for an interpreted or shell language, then it might be desirable to just get the code and then immediately evaluate it:

app = eval(MyPyConstrictor().generate())
app.run()

Feature Tests (0.1)

(look at unittest framework)
(look at compilation tests -- if a particular scenario compiles then the test passes)

Feature test every 0.1 issue that is feasible to test

Custom Arg Parsing Per Implementation

In relation to what's trying to be accomplished in feature #13 and #2, it would be important to allow each custom implementation to help drive what commands it accepts.

For instance, C# has partial classes and can support generated constructions (#2). But, Vala does not and it would be important to omit that from the list of supported commands.

Additionally, the Arg Parsing/Completing stuff needs to be modular and separated out from the code generation. One reason is it adds extra dependency, that if not satisfied shouldn't prevent code from being generated since generating the code doesn't require argcomplete to be in-place

New app command

Implement new command that would create the python DI-config class stubs for a new project.

Ideally, after running the command you could use an IDE like PyCharm and just fill in the missing implementation prompts.

Multi-Lang generation

Given one DI configuration, output the same DI injections in different programming languages.

For instance, if you have different codebases for the same app for different platforms, the DI code can at least be shared/generated from 1 place.

Generate constructors

Generate DI initialization code for go

Golang Constructor-equivalents are tricky, and it would be worthwhile to have this functionality. But for just starting out I'll attempt in C# first. C# partial classes would be less tricky and can benefit from being generated as well. Ideally all of this code would be generated by constrictor based on the dependency definition provided in python files.

/* Auto-generated by Constrictor DI Generator Framework*/
namespace CtorTester
{
    partial class ReadWriter
    {
        private readonly Reader _reader;
        private readonly Writer _writer;
        public ReadWriter(Reader reader, Writer writer)
        {
            _reader = reader;
            _writer = writer;
        }
    }
    partial class Reader
    {
        private readonly IO _io;
        public Reader(IO io)
        {
            _io = io;
        }
    }
    partial class Writer
    {
        private readonly IO _io;
        public Writer(IO io)
        {
            _io = io;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var readWrite = new ReadWriter(new Reader(new IO()), new Writer(new IO()));
            readWrite.Write("Hello");
            if (readWrite.Read().Trim().ToLower()=="world")
            {
                readWrite.Write("woot!");
            }
        }
    }
}

Sharing an object instance

It would be useful to allow opt-in for object sharing in certain circumstances.

Contrived Example:

Let's say you have a Logger class that upon initialization will create a new timestamped file that it will log messages to. For a single run of the app you want all your app components to log to the same file created for that particular run and not in different files. And let's say that you make use of an underlying framework that you provide a logger to, but you don't want all the framework logs mixed in with your own file log, so in that case you opt out of sharing for the instance.

The results would be something like this:

...
    var logger = new Logger();
    new App(logger, new Printer(logger), new Framework(new Logger()));
}

However doing this multiple times, will need to consider different names for each instance. Consider if there were 2 frameworks and you share the logger:

...
    var loggerA = new Logger();
    var loggerB = new Logger();
    new App(loggerA, new Printer(loggerA), new Framework(loggerB), new Framework2(loggerB));
}

Arg Parse and Completions

WSL Ubuntu, steps to get argcomplete working

my-awesome-script

#!/usr/bin/env python3
# PYTHON_ARGCOMPLETE_OK
import argcomplete, argparse

def github_org_members(prefix, parsed_args, **kwargs):
    return ['member_a', 'some_b']

parser = argparse.ArgumentParser()
parser.add_argument("--organization", help="My Org")
parser.add_argument("--member", help="Foo Members").completer = github_org_members
argcomplete.autocomplete(parser)
args = parser.parse_args()

Ubuntu WSL shell

#  DID NOT WORK:
#  activate-global-python-argcomplete

# DID WORK:
export SET PATH="$PATH:/home/korey/source/repos/testers"  # path where my-awesome-script resides
chmod +x my-awesome-script
# I don't think these 2 comands below were necessary
# complete | grep my-awesome-script
# complete -F _minimal my-awesome-script

# running this command shows:
# complete | grep my-awesome-script
#      complete -F _minimal my-awesome-script
eval "$(register-python-argcomplete my-awesome-script)"  # this was the necessary thing that finally worked
#  now running shows:
# complete | grep my-awesome-script
#      complete -o default -o nospace -F _python_argcomplete my-awesome-script

Tab completions work:

my-awesome-script --m => [TAB] => my-awesome-script --member
my-awesome-script --member  => [TAB]
    member_a some_b

see https://pypi.org/project/argcomplete/ , might need to check for prefix startswith

Originally posted by @koreyhinton in #2 (comment)

Template Alternative

Generated code should be modified in-place, as a better alternative to using a template generation source.

One way to accomplish this would be able to read from piped input, modify the string and return it with the replacements.

This could be done for separate files:

cat Containers.cs | my_cs_constrictor containers > Containers.cs
cat Construction.cs | my_cs_constrictor construction > Construction.cs
cat Constructors.cs | my_cs_constrictor constructors > Constructions.cs

Or all in one file, possibly by specifying multiple arguments when it receives the piped input:

cat Wiring.cs | my_cs_constrictor constructors containers construction > Wiring.cs
// Wiring.cs
namespace MyProgram
{
    /*@constructors*/
    /*@containers*/
    public class Wiring
    {
        public App GetApp()
        {
            return /*@construction*/;
        }
    }
}

And then after a first-time replacement, the code would compile from that point on and still be able to receive replacements from any changes done to the dependency configuration. Notice the /*@constructors*/ tag is repeated to mark the start/end mark of what gets replaced.

// Wiring.cs
namespace MyProgram
{
    /*@constructors*/
    public partial class Printer
    {
        private Logger _logger;
        public Printer(LogContainer logContainer)
        {
            _logger = logContainer.Get();
        }
    }
    public partial class App
    {
        private readonly LogContainer _logContainer;
        private readonly Printer _printer;
        public App(Printer printer, LogContainer logContainer)
        {
           _printer = printer;
            _logContainer = logContainer;
        }
    }
    public partial class Printer
    {
        private readonly Logger _logger;
        public Printer(LogContainer logContainer)
        {
            _logger = logContainer.Get();
        }
    }
    /*@constructors*/
    /*@containers*/
    public class LogContainer : Container<Log>
    {
        private static readonly Log _sharedInstance = new Log();
        public Log Get() => _sharedInstance;
    }
    /*@containers*/
    public class Wiring
    {
        public App GetApp()
        {
            return /*@construction*/new App(new Printer(new LogContainer()), new LogContainer())/*@construction*/;
        }
    }
}

Since Log contains no injected-dependencies, it doesn't need a partial class constructor to be generated for it.

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.