env-contract 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 46b38eb0da54bf37b977e0c51e22b5c6173b830cc8bdf7fa84aff5512c90c0f6
4
+ data.tar.gz: 500cc89553cd30513717f8c67ea3e0a4050028fc3d0f032b2854e13455358ba4
5
+ SHA512:
6
+ metadata.gz: 2ac55fbc1957dc8aad824319ad41f7ad41af217ded3857acf05ce97e4181da3cc19082e9573bf306ff2ee5dfb379b65b2f3a6e50559f326c99db3dd8a9e29e80
7
+ data.tar.gz: cda4fe1b60dcd5263acb28096be359b7c0b8d350e0258204b26388738c4d54de75fc8ea3b7dd678d04bff256a53e543954eb0f4f6a1d3854f913292ddf88aac0
@@ -0,0 +1,19 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ ruby: ["3.0", "3.1", "3.2", "3.3"]
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby }}
18
+ bundler-cache: true
19
+ - run: bundle exec rspec
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ # bundler
14
+ Gemfile.lock
15
+
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+ - Initial release.
@@ -0,0 +1,26 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+ We pledge to make participation in our community a harassment-free experience for everyone.
5
+
6
+ ## Our Standards
7
+ Examples of behavior that contributes to a positive environment include:
8
+ - Using welcoming and inclusive language
9
+ - Respecting differing viewpoints and experiences
10
+ - Gracefully accepting constructive criticism
11
+
12
+ Examples of unacceptable behavior include:
13
+ - Trolling, insulting/derogatory comments, and personal or political attacks
14
+ - Public or private harassment
15
+
16
+ ## Enforcement Responsibilities
17
+ Project maintainers are responsible for clarifying and enforcing standards of acceptable behavior.
18
+
19
+ ## Scope
20
+ This Code of Conduct applies within all project spaces and in public spaces when representing the project.
21
+
22
+ ## Enforcement
23
+ Instances of abusive behavior may be reported to the maintainer listed in the gemspec.
24
+
25
+ ## Attribution
26
+ This Code of Conduct is adapted from the Contributor Covenant, version 1.4.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,10 @@
1
+ # Contributing
2
+
3
+ ## Development setup
4
+ - Run `bundle install`
5
+ - Run `bundle exec rspec`
6
+
7
+ ## Release
8
+ 1. Update `CHANGELOG.md`
9
+ 2. Bump version in `lib/**/version.rb`
10
+ 3. Run `bundle exec rake release`
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in env-contract.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 MounirGaiby
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Env Contract
2
+
3
+ Typed ENV contracts with validation and sample generation.
4
+
5
+ ## About
6
+ Env Contract lets you define required and optional environment variables with types, defaults, and descriptions. It validates values at boot time and provides a `env-contract` CLI to generate `.env.sample` output.
7
+
8
+ This is useful for services that need clear, enforceable configuration contracts and easy onboarding.
9
+
10
+ ## Use Cases
11
+ - Enforce required configuration at boot time
12
+ - Generate `.env.sample` files for onboarding and docs
13
+ - Prevent misconfigurations in multi-env deployments
14
+
15
+ ## Compatibility
16
+ - Ruby 3.0+
17
+
18
+ ## Installation
19
+ ```ruby
20
+ # Gemfile
21
+
22
+ gem "env-contract"
23
+ ```
24
+
25
+ ## Usage
26
+ ```ruby
27
+ EnvContract.define do
28
+ required :DATABASE_URL, type: :string, desc: "Primary DB"
29
+ optional :REDIS_URL, type: :string
30
+ optional :RETRIES, type: :integer, default: 3
31
+ end
32
+
33
+ values = EnvContract.load!
34
+ ```
35
+
36
+ ## Types
37
+ - `:string`
38
+ - `:integer`
39
+ - `:float`
40
+ - `:boolean`
41
+ - `:json`
42
+
43
+ ## CLI
44
+ Generate `.env.sample`:
45
+ ```bash
46
+ env-contract > .env.sample
47
+ ```
48
+
49
+ ## Rails
50
+ ```ruby
51
+ # config/application.rb
52
+ config.env_contract.validate_on_boot = true
53
+ ```
54
+
55
+ ## Release
56
+ ```bash
57
+ bundle exec rake release
58
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "env/contract"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/env-contract ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "env/contract"
5
+
6
+ puts EnvContract.sample
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "env/contract/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "env-contract"
9
+ spec.version = EnvContract::VERSION
10
+ spec.authors = ["MounirGaiby"]
11
+ spec.email = ["mounirgaiby@gmail.com"]
12
+
13
+ spec.summary = "Typed ENV contracts with validation and sample generation."
14
+ spec.description = "Define required/optional ENV variables with type casting and validation."
15
+ spec.homepage = "https://github.com/elysium-arc/env-contract"
16
+ spec.license = "MIT"
17
+ spec.required_ruby_version = ">= 3.0"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/elysium-arc/env-contract"
21
+ spec.metadata["changelog_uri"] = "https://github.com/elysium-arc/env-contract/blob/main/CHANGELOG.md"
22
+ spec.metadata["rubygems_mfa_required"] = "true"
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = "bin"
28
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+
32
+
33
+ spec.add_development_dependency "bundler", ">= 1.17"
34
+ spec.add_development_dependency "rake", ">= 13.0"
35
+ spec.add_development_dependency "rspec", "~> 3.12"
36
+ spec.add_development_dependency "simplecov", "~> 0.22"
37
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ begin
5
+ require "rails/railtie"
6
+ rescue LoadError
7
+ end
8
+
9
+ if defined?(Rails::Railtie)
10
+ module EnvContract
11
+ class Railtie < Rails::Railtie
12
+ config.env_contract = ActiveSupport::OrderedOptions.new
13
+ config.env_contract.validate_on_boot = false
14
+
15
+ initializer "env_contract.validate" do |app|
16
+ if app.config.env_contract.validate_on_boot
17
+ EnvContract.load!
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ # :nocov:
@@ -0,0 +1,7 @@
1
+ module EnvContract
2
+ VERSION = "0.1.0"
3
+ end
4
+
5
+ module Env
6
+ Contract = EnvContract
7
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "env/contract/version"
5
+
6
+ module EnvContract
7
+ class Error < StandardError; end
8
+ class MissingVariable < Error; end
9
+ class InvalidType < Error; end
10
+
11
+ Entry = Struct.new(:name, :required, :type, :default, :desc, keyword_init: true)
12
+
13
+ def self.define(&block)
14
+ registry.instance_eval(&block)
15
+ registry
16
+ end
17
+
18
+ def self.registry
19
+ @registry ||= Registry.new
20
+ end
21
+
22
+ def self.reset!
23
+ @registry = Registry.new
24
+ end
25
+
26
+ def self.load!
27
+ registry.load!
28
+ end
29
+
30
+ def self.sample
31
+ registry.sample
32
+ end
33
+
34
+ class Registry
35
+ def initialize
36
+ @entries = []
37
+ end
38
+
39
+ def required(name, type: :string, default: nil, desc: nil)
40
+ add(name, required: true, type: type, default: default, desc: desc)
41
+ end
42
+
43
+ def optional(name, type: :string, default: nil, desc: nil)
44
+ add(name, required: false, type: type, default: default, desc: desc)
45
+ end
46
+
47
+ def load!
48
+ values = {}
49
+ @entries.each do |entry|
50
+ raw = ENV[entry.name]
51
+ if raw.nil? || raw == ""
52
+ if entry.required && entry.default.nil?
53
+ raise MissingVariable, "Missing ENV #{entry.name}"
54
+ end
55
+ raw = entry.default
56
+ end
57
+ values[entry.name] = cast(raw, entry.type)
58
+ end
59
+ values
60
+ end
61
+
62
+ def sample
63
+ lines = []
64
+ @entries.each do |entry|
65
+ lines << "# #{entry.desc}" if entry.desc
66
+ value = entry.default.nil? ? "" : entry.default
67
+ lines << "#{entry.name}=#{value}"
68
+ end
69
+ lines.join("
70
+ ") + "
71
+ "
72
+ end
73
+
74
+ private
75
+
76
+ def add(name, required:, type:, default:, desc:)
77
+ @entries << Entry.new(name: name.to_s, required: required, type: type, default: default, desc: desc)
78
+ end
79
+
80
+ def cast(value, type)
81
+ return nil if value.nil?
82
+ case type
83
+ when :string
84
+ value.to_s
85
+ when :integer
86
+ Integer(value)
87
+ when :float
88
+ Float(value)
89
+ when :boolean
90
+ return true if value == true || value.to_s.downcase == "true"
91
+ return false if value == false || value.to_s.downcase == "false"
92
+ raise InvalidType, "Invalid boolean #{value}"
93
+ when :json
94
+ JSON.parse(value.to_s)
95
+ else
96
+ raise InvalidType, "Unknown type #{type}"
97
+ end
98
+ rescue ArgumentError, TypeError
99
+ raise InvalidType, "Invalid #{type} #{value}"
100
+ end
101
+ end
102
+ end
103
+
104
+ require "env/contract/railtie"
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: env-contract
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - MounirGaiby
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.22'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.22'
69
+ description: Define required/optional ENV variables with type casting and validation.
70
+ email:
71
+ - mounirgaiby@gmail.com
72
+ executables:
73
+ - console
74
+ - env-contract
75
+ - setup
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".github/workflows/ci.yml"
80
+ - ".gitignore"
81
+ - ".rspec"
82
+ - CHANGELOG.md
83
+ - CODE_OF_CONDUCT.md
84
+ - CONTRIBUTING.md
85
+ - Gemfile
86
+ - LICENSE.txt
87
+ - README.md
88
+ - Rakefile
89
+ - bin/console
90
+ - bin/env-contract
91
+ - bin/setup
92
+ - env-contract.gemspec
93
+ - lib/env/contract.rb
94
+ - lib/env/contract/railtie.rb
95
+ - lib/env/contract/version.rb
96
+ homepage: https://github.com/elysium-arc/env-contract
97
+ licenses:
98
+ - MIT
99
+ metadata:
100
+ homepage_uri: https://github.com/elysium-arc/env-contract
101
+ source_code_uri: https://github.com/elysium-arc/env-contract
102
+ changelog_uri: https://github.com/elysium-arc/env-contract/blob/main/CHANGELOG.md
103
+ rubygems_mfa_required: 'true'
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '3.0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubygems_version: 3.3.3
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Typed ENV contracts with validation and sample generation.
123
+ test_files: []