{"id":15391771,"url":"https://github.com/jasononeil/dodrugs","last_synced_at":"2026-01-07T16:02:58.638Z","repository":{"id":140331467,"uuid":"61341126","full_name":"jasononeil/dodrugs","owner":"jasononeil","description":"A macro-powered dependency injector for Haxe","archived":false,"fork":false,"pushed_at":"2018-05-28T18:18:15.000Z","size":144,"stargazers_count":29,"open_issues_count":3,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-27T23:22:18.867Z","etag":null,"topics":["dependency-injection","haxe","injector","macros"],"latest_commit_sha":null,"homepage":null,"language":"Haxe","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jasononeil.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-06-17T02:53:13.000Z","updated_at":"2018-05-28T18:18:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"82893067-c303-4046-a409-25d64dfc957e","html_url":"https://github.com/jasononeil/dodrugs","commit_stats":{"total_commits":100,"total_committers":3,"mean_commits":"33.333333333333336","dds":"0.020000000000000018","last_synced_commit":"c195961fe0631cd8b4aa12c4f694f3c6006c0f8d"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/jasononeil/dodrugs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasononeil%2Fdodrugs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasononeil%2Fdodrugs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasononeil%2Fdodrugs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasononeil%2Fdodrugs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jasononeil","download_url":"https://codeload.github.com/jasononeil/dodrugs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasononeil%2Fdodrugs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28236186,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2026-01-07T02:00:05.975Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","haxe","injector","macros"],"created_at":"2024-10-01T15:12:48.815Z","updated_at":"2026-01-07T16:02:58.601Z","avatar_url":"https://github.com/jasononeil.png","language":"Haxe","funding_links":[],"categories":[],"sub_categories":[],"readme":"DoDrugs (A macro-powered dependency injector for Haxe)\n======================================================\n\n[![Travis Build Status](https://travis-ci.org/jasononeil/dodrugs.svg?branch=master)](https://travis-ci.org/jasononeil/dodrugs)\n\nDoDrugs is a dependency injection (*get it!!*) library for Haxe.\n\nUnlike doing actual drugs, it is safe, because it uses macros to check all of your dependencies at compiletime.\n\n## Usage\n\n### Installation and boilerplate:\n\nInstallation:\n\n\thaxelib install dodrugs\n\nAdd it to your build hxml file:\n\n\t-lib dodrugs\n\nAnd this import:\n\n\timport dodrugs.Injector;\n\n### Set up your injector and its mappings in one place:\n\nAll of your dependencies must be defined in one go:\n\n```haxe\nvar appInjector = Injector.create(\"myapp\", [\n\t// Map a plain value.\n\t// When the Injector is asked for a String named \"apiKey\", we\n\t// will return the String \"my secret\". Same for the other values.\n\tvar apiKey:String = \"my secret\",\n\tvar sessionExpiry:Int = 3600,\n\tvar mysqlCnx:Connection = existingMysqlCnx,\n\n\t// Map a class.\n\t// When the injector is asked for a `MailApi` class named `mailApi`,\n\t// we will build a new instance of the `MyMailApi` class using the\n\t// Injector and return the new instance.\n\tvar mailApi:MailApi = @:toClass MyMailApi,\n\n\t// Map a singleton.\n\t// If it's a singleton, we will use the same instance each time\n\t// it is requested, rather than build multiple instances of the class.\n\t// The first time an `IMailer` named \"mailer\" is requested, we will\n\t// build a new SmtpMailer instance. We'll use that same instance for\n\t// all future requests too.\n\tvar mailer:IMailer = @:toSingletonClass SmtpMailer,\n\n\t// Map a function.\n\t// Provide a function that returns executes and returns a value each time.\n\t// In this example, when a `ReactComponent` named `page` is requested,\n\t// we will run the JSX snippet and return the value.\n\tvar page:ReactComponent = @:toFunction function (inj, id) {\n\t\treturn jsx('\u003cPage\u003e\u003c/Page\u003e');\n\t}\n\n\t// Map a singleton function.\n\t// Provide a function that executes and returns a value the first time,\n\t// and keeps that value for future requests. In this example, when a\n\t// `Connection` named \"cnx\" is requested the first time, we create the\n\t// connection, then we will re-use that connection for all future requests.\n\tvar cnx:Connection = @:toSingletonFunction function (inj, id) {\n\t\treturn Mysql.connect({/**/});\n\t}\n\n\t// Use a wildcard mapping.\n\t// If you want your mapping to match any request for a `Connection`,\n\t// regardless of it's name, use `var _:Connection` to create\n\t// a wildcard mapping.\n\tvar _:Connection = existingMysqlCnx,\n\tvar _:MailApi = @:toClass MyMailApi,\n\n\t// Simple singleton mappings.\n\t// The most common type of mapping for APIs and Services is probably\n\t// `@:toSingletonClass`. If a mapping is simply `MyMailApi` we will\n\t// treat it the same as `var _:MyMailApi = @:toSingletonClass MyMailApi`.\n\tMyMailApi,\n\tSmtpMailer,\n\n\t// Simple class mappings.\n\t// If you would like to map a class to itself, but not as a singleton,\n\t// you can provide the class name and `@:toClass` metadata.\n\t// `@:toClass MyMailApi` is treated the same as\n\t// `var _:MyMailApi = @:toClass MyMailApi`\n\t@:toClass MyMailApi,\n\t@:toClass SmtpMailer\n]);\n\n$type(appInjector); // Injector\u003c\"myapp\"\u003e\n```\n\n### Ask for some things rom the injector:\n\nConstructor injection:\n\n```haxe\n\nclass InjectAConnection {\n\t// Inject a Connection called \"cnx\", or fallback to any Connection.\n\tpublic function new(cnx:sys.db.Connection) {\n\t\tthis.cnx = cnx;\n\t}\n}\n\nclass InjectAString {\n\t// Inject a String called \"assetPath\", or fallback to any String.\n\tpublic function new(assetPath:String) {\n\t\tthis.path = assetPath;\n\t}\n}\n\nclass InjectBothAConnectionAndAString {\n\t// You can inject as many things as you want in the constructor.\n\tpublic function new(cnx:Connection, assetPath:String) {\n\t\tthis.cnx = cnx;\n\t\tthis.path = assetPath;\n\t}\n}\n```\n\nManual injection:\n\n```haxe\n// Request a class, no matter what name:\nvar cnx = appInjector.get(Connection);\nvar mailer = appInjector.get(ufront.mail.UFMailer);\n\n// or:\nvar cnx = appInjector.get(var _:Connection);\nvar mailer = appInjector.get(var _:ufront.mail.UFMailer);\n\n// Request a value with a specific name:\nvar sessionName = appInjector.get(var sessionName:String));\nvar sessionExpiry = appInjector.get(var sessionExpiry:Int);\n\n// Type parameters:\nvar myArray = appInjector.get(var _:Array\u003cString\u003e);\nvar magicNumbers = appInjector.get(var _:magicNumbers:Array\u003cInt\u003e);\n\n// Please note the following will not work, because\n// it is not valid Haxe syntax:\n//     var myArray = appInjector.get(Array\u003cString\u003e);\n// If you need type parameters, you need to use the \"var\" syntax.\n```\n\n### Feel safe:\n\nDoDrugs will not let you compile if a dependency that is required is not supplied.\n\nYou will get an error message like this:\n\n\ttest/Example.hx:30: lines 30-35 : Warning : Mapping \"Array.Array\u003cStdTypes.Int\u003e\" is required here\n\ttest/Example.hx:11: lines 11-15 : Please make sure you provide a mapping for \"Array.Array\u003cStdTypes.Int\u003e\" here\n\n### Child injectors\n\nSometimes it is useful to have child injectors, which share all the same mappings as a parent, as well as some mappings of it's own.\n\n#### `Injector.extend(name, [])`\n\nTo create a child, use `Injector.extend`:\n\n```haxe\nvar requestInjector = Injector.extend(\"request_injector\", appInjector, [\n\t// All of the mappings we defined above in `appInjector` will be available here.\n\t// But we can add some more:\n\tvar user: User = getCurrentUser(),\n\tvar session: Session = getCurrentSession(),\n\tvar req: Request = req,\n\tvar res: Response = res,\n]);\n```\n\n#### `injector.quickExtend([...additionalMappings])`\n\nIf you would like to quickly add a few extra mappings and use an injector, and don't plan to use the injector later, you can use `quickExtend()`:\n\n```haxe\nvar requestInjector = appInjector.quickExtend([\n\tvar req: Request = currentRequest,\n\tvar res: Response = currentResponse,\n]);\nvar user = requestInjector.get(User);\n```\n\nUsing `quickExtend()` will generate an injector name automatically, so it is inconvenient to use the new injector in another function at a later time.\nIt is designed to be used immediately.\n\n#### `injector.getWith(RequestedType, [...additionalMappings])`\n\nIf you would like to fetch a single value from an injector, while adding a few extra mappings, you can use `injector.getWith()`.\n\nCalling `injector.getWith(type, mappings)` is essentially the same as calling `injector.quickExtend(mappings).get(type);`.\n\n```haxe\nvar user = appInjector.getWith(User, [\n\tvar req: Request = currentRequest,\n\tvar res: Response = currentResponse\n]);\n```\n\n#### `injector.instantiate(RequestedClass)`\n\nIf you would like to create a new object of a particular class using the injector, but the class does not have a mapping, you can use `injector.instantiate(RequestedClass)`:\n\n```haxe\nvar inj = Injector.create('app', [\n\tvar name: String = 'Jason',\n\tvar age: Int = 30\n]);\n// This will work even though \"Person\" was not mapped in the 'app' injector.\nvar person = inj.instantiate(Person);\n```\n\nThis works by creating a child injector with an extra mapping for that class. It is essentially the same as calling:\n\n```haxe\nvar person = inj\n\t.quickExtend([\n\t\tvar _:Person = @:toClass Person\n\t])\n\t.get(Person);\n```\n\nNote: if you call `instantiate()` but a mapping for the class already existed, the existing mapping will be used.\n\n#### A note about singletons and child injectors\n\nA singleton is created the first time `injector.get(MySingleton)` is called, and it will be available for future requests on that injector, and on all children injectors. Therefore, if you have a singleton mapping on a parent injector:\n\n- If you call `parent.get(MySingleton)`, the `MySingleton` object will be created and shared between the parent and all children.\n- If you call `child.get(MySingleton)`, the `MySingleton` object will be created and re-used for that child and any of it's children/grandchildren.\n- If you call both, the behaviour will change depending on which one you call first. If `parent.get(MySingleton)` is called before `child.get(MySingleton)`, they will share the same object. If the child is called first, it will have its own scoped object.\n\nThis design trade-off was chosen as part of a refactor to allow children to supply injections to the parent injectors.\nIf you have advice on a more predictable API pattern we could use here, please open an issue so we can discuss.\n\n## Concepts\n\n 1. #### Each injector has a unique name, and we know exactly what mappings it has at compile time, so we can be sure it has all the mappings it needs.\n\n\tThis is how we add compile time safety.\n\n\tAnytime you have an `Injector\u003c\"app\"\u003e` it will only be able to use the mappings available when `Injector.create(\"app\", [])` was used.\n\n\tThe idea of having a String as a type parameter is pretty odd, but it was the most light-weight way I could find to track injections accurately.\n\n 2. #### We only offer constructor injection and manual injection.\n\n \tUnlike [minject](https://github.com/massiveinteractive/minject/), another popular dependency injection library for Haxe, we do not support `@inject` injection points on variables or methods, and we do not have `@post` injection hooks.  You can only inject into the constructor, and everything will be available immediately.\n\n\tIf you really would prefer property injection points, you can use [tink_lang](https://haxetink.github.io/tink_lang/#/declaration-sugar/property-declaration?id=direct-initialization) to automatically make variables things that are set in the constructor:\n\n\t```haxe\n\t@:tink class Person {\n\t\tvar name:String = (\"Stranger\"); // Will become a constructor argument, default value is \"Stranger\".\n\t\tvar age:Int = _;                // Will become a constructor argument, with no default value.\n\t\tfunction new() {}\n\t}\n\t```\n\n 3. #### Avoid reflection.\n\n\tUsing runtime reflection adds a lot of bloat to Haxe generated JS. ([Here is a simple gist](https://gist.github.com/jasononeil/bf5da8e176e595f476720ffffa6816b9) showing an example with the generated JS).\n\n\tOur aim is to avoid using `Reflect.callMethod`, `Reflect.setProperty`, `Reflect.fields`, `Type.getInstanceFields` or similar methods. We do this by using macros to generate code for instantiating new objects, rather than figuring it out at runtime using reflection.\n\n\tTake a look at `bin/example.js` - it is very obvious when looking at the output code how each object is being constructed.\n\tThat example only has about 100 lines of generated JS - quite tiny considering a full dependency injector is in use.\n\n\tPlease note we do use `DynamicAccess`, which on some Haxe targets will use reflection, but importantly the output is clean and avoids reflection on the JS target.\n\n 4. #### No runtime dependencies.\n\n\tWe have a compile time dependency on `tink_core` and `tink_macro`.\n\tThese are not included in the generated code.\n\n\tAgain, look at the generated `bin/example.js` to see how compact the resulting code can be.\n\n## About the project\n\n### License\n\nAll code is released under the MIT license.\n\n### Support\n\nIf you find a bug or need help, feel free to post a Github issue.\n\n### Contributions\n\nBug fixes and new features are welcome, providing they keep in line with the concepts given above, and the code stays small and focused.\n\nIf you submit a pull request, and you've made sure to update the tests and check they are passing, I will be your friend :)\n\n### Naming\n\nThis is an injection library for Haxe that uses macros for extra safety, to avoid runtime issues. I thought about calling it \"macro inject\", or \"minject\" for short, but that was [already taken](https://github.com/massiveinteractive/minject/).\n\nSo I searched for \"[synonym inject](https://duckduckgo.com/?q=synonym+inject\u0026ia=thesaurus)\" and settled on the name \"do drugs\".\n\nI feel that the first time you understand dependency injection, it blows your mind. Comprehending Haxe macros is also a mind altering experience. Therefore using both macros and dependency injection at the same time must be the hard stuff.\n\nSome people may be offended by the name. And being offensive is how you become a famous person or a presidential nominee.\n\nDisclaimer: I've not personally taken illegal drugs. While some are probably fine others are life ruining. Next time you're tempted to take illicit substances, just type `haxe -lib dodrugs` instead.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjasononeil%2Fdodrugs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjasononeil%2Fdodrugs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjasononeil%2Fdodrugs/lists"}