{"id":16443379,"url":"https://github.com/0exp/siege","last_synced_at":"2025-06-19T18:33:38.347Z","repository":{"id":56895579,"uuid":"228272591","full_name":"0exp/siege","owner":"0exp","description":"Siege - software architecture principles realized as a code (modular application skeleton, instrumentation tooling and much more)","archived":false,"fork":false,"pushed_at":"2020-05-24T17:10:04.000Z","size":121,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-09T04:06:16.410Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/0exp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-16T00:25:52.000Z","updated_at":"2024-02-26T22:18:40.000Z","dependencies_parsed_at":"2022-08-21T01:20:51.754Z","dependency_job_id":null,"html_url":"https://github.com/0exp/siege","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsiege","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsiege/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsiege/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsiege/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0exp","download_url":"https://codeload.github.com/0exp/siege/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240795007,"owners_count":19858725,"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","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":[],"created_at":"2024-10-11T09:20:17.093Z","updated_at":"2025-02-26T05:16:23.035Z","avatar_url":"https://github.com/0exp.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Siege \u0026middot; [![Gem Version](https://badge.fury.io/rb/siege.svg)](https://badge.fury.io/rb/siege) [![Build Status](https://travis-ci.org/0exp/siege.svg?branch=master)](https://travis-ci.org/0exp/siege)\n\nSoftware architecture principles realized as a code.\n\n## Installation\n\n```ruby\ngem 'siege'\n```\n\n```shell\n$ bundle install\n# --- or ---\n$ gem install 'siege'\n```\n\n```ruby\nrequire 'siege'\n```\n\n---\n\n# Usage\n\n- [Modular Application Skeleton](#siegesystem)\n- [Generic Instrumenter](#siegetoolinginstrumentation)\n\n\n---\n\n## Siege::System\n\nApplication-wide infrastructure service that incapsulates the core functionality of your system.\n\n```yaml\n# database.yml\nhost: localhost\n```\n\n```ruby\nclass Infrastructure \u003c Siege::System\n  element(:database) do\n    configuration do\n      setting :db_config\n      setting :db_address\n      values_file 'database.yml'\n    end\n\n    init { require 'sequel'; register(:database, Sequel.build_connection(config['host']) } }\n    start { database.connect! }\n    stop { database.disconnect! }\n\n    after_init { puts '[database] initialized' }\n    after_start { puts '[database] started' }\n    after_stop { puts '[database] stopped' }\n\n    # before_init {}\n    # before_start {}\n    # before_stop {}\n  end\n\n  element(:logger) do\n    init { require 'logger' }\n    start { register(:logger, Logger.new(STDOUT) } }\n    stop { logger.info('[logger] stopped') }\n  end\nend\n\n# instantiate with initial configs\napp_instance = Infrastructure.build_instance do |settings|\n  # hash-based configuration is supported too\n  settings.configure(:database, { db_address: '1.2.3.4' }) do |config|\n    config.db_address = '127.0.0.1'\n  end\nend\n# =\u003e #\u003cInfrastructure:0x00007f81884d7310\u003e\n\n# runtime configuration\napp_instance.configure(:database) do |config|\n  config.db_address = '5.5.5.5'\nend\n\n# hash-based configs\napp_instance.configure(:database, { db_address: '7.7.7.7' }) do |config|\n  # and etc...\nend\n```\n\nCustom element loader example:\n\n```ruby\nclass LoggerLoader \u003c Siege::System::Loader\n  init { require 'logger' }\n  start { register(:logger) { Logger.new(STDOUT) } }\n  stop { logger.info('[logger] stopped') }\nend\n\nclass Application \u003c Siege::System\n  element(:logger, loader: LoggerLoader)\nend\n\napp_instance = Application.build_instance # =\u003e #\u003cApplication:0x00007f0f0f1d6332\u003e\n```\n\nYou can use one element entity from another:\n  - if required element has not being started yet - it will be started;\n  - you can provde `as:` option with the name of the custom access method (element entity's name is used by default);\n  - the element entity name is a string with two parts separated by `.`-symbol: `element_name.entity_name`;\n\n```ruby\nclass Infrastructure \u003c Siege::System\n  element(:database) do\n    init do\n      use 'logging.logger', as: :log # .log\n      use 'alerts.notifier' # .notifier\n\n      log.info('test')\n      notifier.call('notification')\n    end\n  end\n\n  element(:logging) do\n    init {}\n    start { register(:logger, Logger.new(STDOUT)) } # entity registration\n  end\n\n  element(:alerts) do\n    init { register(:notifier, Notifier.new) } # entity registration\n  end\nend\n\napp_instance = Infrastructure.build_instance\n\napp_instance.init(:database)\napp_instance.status\n# =\u003e\n{ 'database' =\u003e :initialized, 'logging' =\u003e :started, 'alerts' =\u003e :started }\n```\n\nResolve registered element entities (you should provide both element name and entity name):\n\n```ruby\nclass Infrastructre \u003c Siege::System\n  element(:database) do\n    init { register(:db) { DBClient.new } }\n  end\n\n  element(:logging) do\n    start { register(:logger) { Logger.new(STDOUT) } }\n  end\nend\n\ninfrastructure = Infrastructre.build_instance\n\ninfrastructure.init\ninfrastructure['database.db'] # =\u003e #\u003cDBClient:0x00007f1f991d7701\u003e\n\ninfrastructure.start(:logger)\ninfrastructure['logging.logger'] # =\u003e #\u003cLogger:0x00007f1f991d7702\u003e\n\n# All registered entities:\ninfrastructure.entities\n# =\u003e\n{\n  'database.db' =\u003e #\u003cDBClient:0x00007f1f991d7701\u003e,\n  'logging.logger' =\u003e #\u003cLogger:0x00007f1f991d7702\u003e\n}\n```\n\nSystem's Initialization/Starting/Stopping processes:\n\n```ruby\napp_instance.init # initialize all elements\napp_instance.init(:logger) # initialize logger element\n\napp_instance.status\n# =\u003e\n{ 'logger' =\u003e :initialized, 'database' =\u003e :non_initialized }\n\napp_instance.start # start all elements\napp_instance.start(:logger, :database) # start only the logger element\n\napp_instance.status\n# =\u003e\n{ 'logger' =\u003e :started, 'database' =\u003e :started }\n\n# and stop / stop(*element_names) respectively\n```\n\n---\n\n# Siege::Tooling::Instrumentation\n\n- Usage\n\n```ruby\ninstrumenter = Siege::Tooling::Instrumentation.build_instance\n\n# --- subscribe ---\nsubscriber1 = instrumenter.subscribe('*') do |event|\n  # some logic\nend\n\nsubscriber2 = instrumenter.subscribe('user.#') do |event|\n  # some logic\nend\n\nsubscriber3 = instrumenter.subscribe('user.created') do |event|\n  # some logic\nend\n\n# --- instrument ---\ninstrumenter.instrument('user.created') do |payload:, metadata:|\n  payload[:user_id] = 12345\n  metadata[:framework] = 'ActiveRecord'\n  # - subscriber1\n  # - subscriber2\n  # - subscriber3\nend\n\ninstrumenter.instrument('user.updated') do |payload:, metadata:|\n  # - subscriber1\n  # - subscriber2\nend\n\ninstrumenter.instrument('system.fail') do |payload:, metadata:|\n  payload[:module] = 'logger'\n  # - subscriber1\nend\n\n# --- unsubscribe ---\ninstrumenter.unsubscribe(subscriber1)\ninstrumenter.unsubscribe(subscriber2)\ninstrumenter.unsubscribe(subscriber3)\n```\n\n- Event Structure (`Siege::Tooling::Instrumentation::Event`)\n\n```ruby\nevent.id # =\u003e UUID\nevent.name # =\u003e user.created (for example)\nevent.start_time # an instance of Time\nevent.end_time # an instance of Time\nevent.payload # initialized during instrumentation\nevent.metadata # initialized during instrumentation\n\nevent.to_h # =\u003e { id: ?, name: ?, start_time: ?, end_time: ?, payload: ?, metadata: ? }\n```\n\n---\n\n## Roadmap\n\n- async element loading process;\n\n- configurable intialization pipeline:\n\n```ruby\nclass HomeInfrastructre \u003c Siege::System\n  pipeline do\n    pipe(:database)\n    pipe(:logger, async: true)\n    pipeline(:async) do # nested pipeline with async loading\n      pipe(:rack_logger)\n    end\n    pipe(SystemInfrastructure, async: true) # another siege subsystem\n  end\nend\n```\n\n- system composition:\n\n```ruby\nclass Infrastructure \u003c Siege::System\n  sub_system(HomeInfrastructre)\nend\n```\n\n---\n\n## Contributing\n\n- Fork it ( https://github.com/0exp/siege/fork )\n- Create your feature branch (`git checkout -b feature/my-new-feature`)\n- Commit your changes (`git commit -am '[my-new-featre] Add some feature'`)\n- Push to the branch (`git push origin feature/my-new-feature`)\n- Create new Pull Request\n\n## License\n\nReleased under MIT License.\n\n## Authors\n\n[Rustam Ibragimov](https://github.com/0exp)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fsiege","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0exp%2Fsiege","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fsiege/lists"}