Project

siege

0.0
No commit activity in last 3 years
No release in over 3 years
Software architecture principles realized as a code.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.1
~> 13.0
~> 3.9
~> 0.17
 Project Readme

Siege ยท Gem Version Build Status

Software architecture principles realized as a code.

Installation

gem 'siege'
$ bundle install
# --- or ---
$ gem install 'siege'
require 'siege'

Usage

  • Modular Application Skeleton
  • Generic Instrumenter

Siege::System

Application-wide infrastructure service that incapsulates the core functionality of your system.

# database.yml
host: localhost
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
# => #<Infrastructure:0x00007f81884d7310>

# 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:

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 # => #<Application:0x00007f0f0f1d6332>

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;
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):

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'] # => #<DBClient:0x00007f1f991d7701>

infrastructure.start(:logger)
infrastructure['logging.logger'] # => #<Logger:0x00007f1f991d7702>

# All registered entities:
infrastructure.entities
# =>
{
  'database.db' => #<DBClient:0x00007f1f991d7701>,
  'logging.logger' => #<Logger:0x00007f1f991d7702>
}

System's Initialization/Starting/Stopping processes:

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
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)
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:

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:
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