Skip to content

Commit 3818d4d

Browse files
committed
Add README
1 parent 546e765 commit 3818d4d

1 file changed

Lines changed: 198 additions & 0 deletions

File tree

README.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Ginject
2+
3+
> A lightweight, flexible dependency injection framework for Elixir.
4+
5+
![Elixir](https://img.shields.io/badge/Elixir-elixir?logo=elixir&color=%234B275F)
6+
[![Hex.pm](https://img.shields.io/hexpm/v/ginject.svg)](https://hex.pm/packages/ginject)
7+
[![Documentation](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/ginject)
8+
[![Elixir CI](https://github.com/Gigitsu/ginject/workflows/CI/badge.svg)](https://github.com/Gigitsu/ginject/actions)
9+
[![License](https://img.shields.io/hexpm/l/ginject.svg)](https://github.com/Gigitsu/ginject/blob/main/LICENSE)
10+
[![Hex.pm Download Total](https://img.shields.io/hexpm/dt/ginject.svg)](https://hex.pm/packages/ginject)
11+
12+
## About
13+
14+
Ginject is a thoughtfully designed dependency injection framework for Elixir applications that embraces the philosophy of explicit over implicit. It provides a clean, declarative way to manage dependencies while maintaining the flexibility and power that Elixir developers expect.
15+
16+
### Why Ginject?
17+
18+
* **Elixir-centric Design**: Built from the ground up with Elixir's patterns and practices in mind
19+
* **Compile-time Resolution**: Catch configuration issues early with compile-time dependency resolution
20+
* **Flexible Strategy System**: Easily adapt to different dependency resolution needs through configurable strategies
21+
* **Testing First**: First-class support for testing with mock implementations and strategy swapping
22+
* **Zero Runtime Overhead**: All the dependency wiring happens at compile time
23+
* **Safety**: Leverages Elixir behaviours for interface definitions
24+
25+
## Installation
26+
27+
Add `ginject` to your list of dependencies in `mix.exs`:
28+
29+
```elixir
30+
def deps do
31+
[
32+
{:ginject, "~> 0.1.0"}
33+
]
34+
end
35+
```
36+
37+
## Usage
38+
39+
### Basic Usage
40+
41+
There are two ways to inject services into a module:
42+
43+
#### Using the `use` directive with `:services` option
44+
45+
```elixir
46+
defmodule YourApp.UserController do
47+
use Ginject, services: [
48+
YourApp.UserService,
49+
{YourApp.AuthService, as: Auth}
50+
]
51+
52+
def create_user(params) do
53+
with {:ok, user} <- UserService.create(params),
54+
{:ok, token} <- Auth.generate_token(user) do
55+
{:ok, %{user: user, token: token}}
56+
end
57+
end
58+
end
59+
```
60+
61+
#### Using the `service` macro
62+
63+
```elixir
64+
defmodule YourApp.UserController do
65+
use Ginject
66+
67+
service YourApp.UserService
68+
service YourApp.AuthService, as: Auth
69+
70+
def create_user(params) do
71+
with {:ok, user} <- UserService.create(params),
72+
{:ok, token} <- Auth.generate_token(user) do
73+
{:ok, %{user: user, token: token}}
74+
end
75+
end
76+
end
77+
```
78+
79+
### Configuration
80+
81+
Configure Ginject in your application config:
82+
83+
```elixir
84+
# config/config.exs
85+
config :ginject, Ginject.DI,
86+
strategy: Ginject.Strategy.BehaviourAsDefault,
87+
services: [
88+
{YourApp.UserController, [
89+
%{service: YourApp.UserService, impl: YourApp.UserService.Impl},
90+
%{service: YourApp.AuthService, impl: YourApp.AuthService.Impl}
91+
]}
92+
]
93+
```
94+
95+
## Best Practices
96+
97+
### 1. Define Service Interfaces Using Behaviours
98+
99+
```elixir
100+
defmodule YourApp.UserService do
101+
@callback create(params :: map()) :: {:ok, User.t()} | {:error, term()}
102+
@callback update(id :: integer(), params :: map()) :: {:ok, User.t()} | {:error, term()}
103+
end
104+
```
105+
106+
### 2. Keep Service Implementations Isolated
107+
108+
```elixir
109+
defmodule YourApp.UserService.Impl do
110+
@behaviour YourApp.UserService
111+
112+
@impl true
113+
def create(params) do
114+
# Implementation
115+
end
116+
117+
@impl true
118+
def update(id, params) do
119+
# Implementation
120+
end
121+
end
122+
```
123+
124+
### 3. Use Meaningful Aliases
125+
126+
Choose clear and concise aliases that make the code more readable:
127+
128+
```elixir
129+
# Good
130+
service UserManagement.AuthenticationService, as: Auth
131+
service UserManagement.AuthorizationService, as: Permissions
132+
133+
# Avoid
134+
service UserManagement.AuthenticationService, as: A
135+
service UserManagement.AuthorizationService, as: Auth2
136+
```
137+
138+
## Testing
139+
140+
Add `Hammox` dependency in your `mix.exs`:
141+
142+
143+
```elixir
144+
def deps do
145+
[
146+
...,
147+
{:hammox, "~> 0.7", only: :test}
148+
]
149+
end
150+
```
151+
152+
Use the builtin Mox behaviour:
153+
154+
```elixir
155+
# config/test.exs
156+
config :ginject, Ginject.Di, strategy: Ginject.Strategy.Mox
157+
```
158+
159+
While in your test use:
160+
161+
```elixir
162+
defmodule MyTest do
163+
use ExUnit.Case
164+
use Ginject.Test
165+
166+
test "..." do
167+
mock = get_injected_service(MyModule, MyService)
168+
169+
expect(mock, :function_name, fn ->
170+
# assertions
171+
end)
172+
end
173+
end
174+
```
175+
176+
See `Mox` or `Hammox` documentation to learn how to set expectations.
177+
178+
## Contributing
179+
180+
1. Fork the repository
181+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
182+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
183+
4. Push to the branch (`git push origin feature/amazing-feature`)
184+
5. Open a Pull Request
185+
186+
Make sure to:
187+
- Write tests for new features
188+
- Update documentation as needed
189+
- Follow the existing coding style
190+
- Write good commit messages
191+
192+
## License
193+
194+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
195+
196+
## Credits
197+
198+
Created and maintained by [Gigitsu](https://github.com/Gigitsu).

0 commit comments

Comments
 (0)