https://github.com/0exp/siege
Siege - software architecture principles realized as a code (modular application skeleton, instrumentation tooling and much more)
https://github.com/0exp/siege
Last synced: about 1 year ago
JSON representation
Siege - software architecture principles realized as a code (modular application skeleton, instrumentation tooling and much more)
- Host: GitHub
- URL: https://github.com/0exp/siege
- Owner: 0exp
- License: mit
- Created: 2019-12-16T00:25:52.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2020-05-24T17:10:04.000Z (about 6 years ago)
- Last Synced: 2025-02-09T04:06:16.410Z (over 1 year ago)
- Language: Ruby
- Homepage:
- Size: 118 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Siege · [](https://badge.fury.io/rb/siege) [](https://travis-ci.org/0exp/siege)
Software architecture principles realized as a code.
## Installation
```ruby
gem 'siege'
```
```shell
$ bundle install
# --- or ---
$ gem install 'siege'
```
```ruby
require 'siege'
```
---
# Usage
- [Modular Application Skeleton](#siegesystem)
- [Generic Instrumenter](#siegetoolinginstrumentation)
---
## Siege::System
Application-wide infrastructure service that incapsulates the core functionality of your system.
```yaml
# database.yml
host: localhost
```
```ruby
class Infrastructure < Siege::System
element(:database) do
configuration do
setting :db_config
setting :db_address
values_file 'database.yml'
end
init { require 'sequel'; register(:database, Sequel.build_connection(config['host']) } }
start { database.connect! }
stop { database.disconnect! }
after_init { puts '[database] initialized' }
after_start { puts '[database] started' }
after_stop { puts '[database] stopped' }
# before_init {}
# before_start {}
# before_stop {}
end
element(:logger) do
init { require 'logger' }
start { register(:logger, Logger.new(STDOUT) } }
stop { logger.info('[logger] stopped') }
end
end
# instantiate with initial configs
app_instance = Infrastructure.build_instance do |settings|
# hash-based configuration is supported too
settings.configure(:database, { db_address: '1.2.3.4' }) do |config|
config.db_address = '127.0.0.1'
end
end
# => #
# runtime configuration
app_instance.configure(:database) do |config|
config.db_address = '5.5.5.5'
end
# hash-based configs
app_instance.configure(:database, { db_address: '7.7.7.7' }) do |config|
# and etc...
end
```
Custom element loader example:
```ruby
class LoggerLoader < Siege::System::Loader
init { require 'logger' }
start { register(:logger) { Logger.new(STDOUT) } }
stop { logger.info('[logger] stopped') }
end
class Application < Siege::System
element(:logger, loader: LoggerLoader)
end
app_instance = Application.build_instance # => #
```
You can use one element entity from another:
- if required element has not being started yet - it will be started;
- you can provde `as:` option with the name of the custom access method (element entity's name is used by default);
- the element entity name is a string with two parts separated by `.`-symbol: `element_name.entity_name`;
```ruby
class Infrastructure < Siege::System
element(:database) do
init do
use 'logging.logger', as: :log # .log
use 'alerts.notifier' # .notifier
log.info('test')
notifier.call('notification')
end
end
element(:logging) do
init {}
start { register(:logger, Logger.new(STDOUT)) } # entity registration
end
element(:alerts) do
init { register(:notifier, Notifier.new) } # entity registration
end
end
app_instance = Infrastructure.build_instance
app_instance.init(:database)
app_instance.status
# =>
{ 'database' => :initialized, 'logging' => :started, 'alerts' => :started }
```
Resolve registered element entities (you should provide both element name and entity name):
```ruby
class Infrastructre < Siege::System
element(:database) do
init { register(:db) { DBClient.new } }
end
element(:logging) do
start { register(:logger) { Logger.new(STDOUT) } }
end
end
infrastructure = Infrastructre.build_instance
infrastructure.init
infrastructure['database.db'] # => #
infrastructure.start(:logger)
infrastructure['logging.logger'] # => #
# All registered entities:
infrastructure.entities
# =>
{
'database.db' => #,
'logging.logger' => #
}
```
System's Initialization/Starting/Stopping processes:
```ruby
app_instance.init # initialize all elements
app_instance.init(:logger) # initialize logger element
app_instance.status
# =>
{ 'logger' => :initialized, 'database' => :non_initialized }
app_instance.start # start all elements
app_instance.start(:logger, :database) # start only the logger element
app_instance.status
# =>
{ 'logger' => :started, 'database' => :started }
# and stop / stop(*element_names) respectively
```
---
# Siege::Tooling::Instrumentation
- Usage
```ruby
instrumenter = Siege::Tooling::Instrumentation.build_instance
# --- subscribe ---
subscriber1 = instrumenter.subscribe('*') do |event|
# some logic
end
subscriber2 = instrumenter.subscribe('user.#') do |event|
# some logic
end
subscriber3 = instrumenter.subscribe('user.created') do |event|
# some logic
end
# --- instrument ---
instrumenter.instrument('user.created') do |payload:, metadata:|
payload[:user_id] = 12345
metadata[:framework] = 'ActiveRecord'
# - subscriber1
# - subscriber2
# - subscriber3
end
instrumenter.instrument('user.updated') do |payload:, metadata:|
# - subscriber1
# - subscriber2
end
instrumenter.instrument('system.fail') do |payload:, metadata:|
payload[:module] = 'logger'
# - subscriber1
end
# --- unsubscribe ---
instrumenter.unsubscribe(subscriber1)
instrumenter.unsubscribe(subscriber2)
instrumenter.unsubscribe(subscriber3)
```
- Event Structure (`Siege::Tooling::Instrumentation::Event`)
```ruby
event.id # => UUID
event.name # => user.created (for example)
event.start_time # an instance of Time
event.end_time # an instance of Time
event.payload # initialized during instrumentation
event.metadata # initialized during instrumentation
event.to_h # => { id: ?, name: ?, start_time: ?, end_time: ?, payload: ?, metadata: ? }
```
---
## Roadmap
- async element loading process;
- configurable intialization pipeline:
```ruby
class HomeInfrastructre < Siege::System
pipeline do
pipe(:database)
pipe(:logger, async: true)
pipeline(:async) do # nested pipeline with async loading
pipe(:rack_logger)
end
pipe(SystemInfrastructure, async: true) # another siege subsystem
end
end
```
- system composition:
```ruby
class Infrastructure < Siege::System
sub_system(HomeInfrastructre)
end
```
---
## Contributing
- Fork it ( https://github.com/0exp/siege/fork )
- Create your feature branch (`git checkout -b feature/my-new-feature`)
- Commit your changes (`git commit -am '[my-new-featre] Add some feature'`)
- Push to the branch (`git push origin feature/my-new-feature`)
- Create new Pull Request
## License
Released under MIT License.
## Authors
[Rustam Ibragimov](https://github.com/0exp)