0.0
The project is in a healthy, maintained state
MonoRepoDeps is a powerful tool designed to streamline the process of splitting code into separate packages within a monorepository. It provides a flexible and easy-to-use solution for managing dependencies between packages, making it ideal for large-scale projects with complex codebases. Key Features: - Simplifies the organization of code in a monorepo structure. - Manages dependencies seamlessly between different packages. - Enhances collaboration and code sharing among teams working on interconnected components. - Offers a straightforward and customizable configuration for your monorepo setup. How to Use: - Install the gem using `gem install MonoRepoDeps`. - Configure MonoRepoDeps with your monorepo specifications. - Enjoy a more efficient and organized development workflow. Visit the official documentation for detailed instructions and examples. GitHub Repository: https://github.com/droidlabs/MonoRepoDeps
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.2
>= 0
~> 13.0
~> 3.0

Runtime

~> 0.11.0
~> 1.0.0
~> 1.0.1
>= 0
~> 2.6.12
~> 0.5.11164
 Project Readme

MonoRepoDeps

Dependency manager to split and organize Ruby modules and namespaces into highlevel packages and manage relations between them Next version of Rdm gem

Idea

Main goal to implement the gem was to give developers an ability to keep a large codebase in the single git repository (monorepository) with easy access to:

  1. require only needed dependencies
  2. set up loading policies and inflection rules
  3. manage configuration for different environments
  4. run specific tasks on selected packages

Example Project File Structure

  ├── MonoRepoConfig.rb
  ├── Gemfile
  ├── Gemfile.lock
  ├── bounded_contexts
  │   ├── orders
  │   │   ├── bin
  │   │   │   └── run
  │   │   ├── package
  │   │   │   ├── orders
  │   │   │   │   └── export_orders.rb
  │   │   │   │
  │   │   │   └── orders.rb
  │   │   │
  │   │   └── Package.rb
  │   │
  │   ├── orders_db
  │   │   ├── package
  │   │   │   └── orders_db.rb
  │   │   │
  │   │   └── Package.rb
  │   │
  │   └── cart
  │       ├── package
  │       │   └── orders.rb
  │       │
  │       └── Package.rb
  │
  └── configs
      ├── orders_config
      │   ├── default.yml
      │   └── test.yml
      │
      └── api_client_config
          ├── default.yml
          └── test.yml

Package

The main MonoRepoDeps entity is the "package". Package configuration is defined in Package.rb file. Each package also have an 'entrypoint' file, which can be defined as package namespace module.

For example for orders package from project structure above entrypoint file could look the following way:

  module Orders
  end

  require 'some_gem'

  SomeGem.example_initialization_for(__dir__)

You can declare dependencies rules which will determine what packages should be imported before you will initialize current one

  # ./bounded_contexts/orders/Package.rb

  package do
    name    'marketplace'
  end

  dependency do
    import 'orders_db'
  end

  dependency :test do
    import 'cart'
  end

Having that configuration you can call the following code in your bin/run file

  #!/usr/bin/env ruby
  require 'bundler/setup'
  require 'mono_repo_deps'
  MonoRepoDeps.init_package

  Orders::ExportOrders.new.call

Let's take a look at MonoRepoDeps.init_package command. That is the core feature of MonoRepoDeps and it does the following actions

  1. Detect which package the caller file belongs to You can also provide that file directly using 'from:' parameter MonoRepoDeps.init_package(from: __FILE__) or providing package name MonoRepoDeps.init_package('orders')
  2. Build dependencies tree: inside each project we have dependency do; end block which is provide us information which packages should be loaded before the current one
  3. For each file in dependency tree:
    • Using Project Code Loader to autoload/preload package classes and modules
    • Call require for entrypoint file

Configs

MonoRepoDeps Configs provides interface to declare and access key-value storage can be specified for certain environment

Instead of using ENV['SOME_VARIABLE_NAME'] in your ruby files you can set up MonoRepoDeps Configs and use env variables in your code like this:

  MonoRepoDeps.configs.api_client.some_variable_name

Let's define MonoRepoDeps Config for api_client

  # ./configs/api_client/default.yml
  api_client:
    some_variable_name: ENV['SOME_VARIABLE_NAME']

  # ./configs/api_client/test.yml
  api_client:
    some_variable_name: ENV['SOME_VARIABLE_NAME'] + ENV['TEST_ENV_NUMBER']

Rdm looks up for configs in directory have been set in MonoRepoConfig.rb file with set_configs_dir method Each config has at least default.yml configuration which can be complemented with :env.yml files Running MonoRepoDeps.init_package with specified :env will check if corresponding :env.yml file exists and merge it's values to the default configuration

Project

The 2 main entities within MonoRepoDeps project are 'project' and 'package' Project declaration stored in MonoRepoConfig.rb and a parent directory of the file is considered as the project root

Project Configuration File Example

  setup do |project|
    # set up project.env
    set_env(ENV['RUBY_ENV'] || raise('RUBY_ENV environment variable is not set'))

    # set up directory to look up for project configs
    set_configs_dir 'configs'

    # set up directory to look up for packages
    set_package_dirname 'package'
    set_packages_lookup_subdir 'bounded_contexts'

    # code loader configuration
    set_loader :zeitwerk do
      # file patterns to exclude in ruby autoloading
      ignore "**/schema_migrations"
      ignore "**/data_migrations"

      # rules to overwrite default filename-to-class strategy
      overwrite ->(kname) { kname.gsub(/Foo/) { _1.upcase } }
      overwrite ->(kname) { kname.gsub('Baz', 'BazBar') }

      # another way to define inflection rules
      inflect "uuid_generator" => "UUIDGenerator"
    end

    # definition of the task to run rspec on the specific package
    register_task :rspec_run, on: :package do |package, args|
      Dir.chdir(package.root_path) do
        require 'rspec'

        status = RSpec::Core::Runner.run(args, $stderr, $stdout).to_i

        exit(status)
      end
    end
  end

Tasks

MonoRepoDeps tasks are an easy way to declare some procedures can be called on you packages/package

Examples

  # MonoRepoConfig.rb
    setup do |project|
      # ...
      register_task :puts_package_name, on: :package do |package, args|
        puts [package.name, args].inspect
      end
      # ...
    end

You can call defined task that way:

  MonoRepoDeps.tasks.puts_package_name(
    packages: MonoRepoDeps.packages.filter(name: "orders"),
    args: [1, 2, 3]
  )