state_sync 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: c0168fcb451c250ca0603039b16d46b17549155e16220022d42659525492c583
4
+ data.tar.gz: 799020c89366e8c271eb64c403004f545fdc0703cf5ac6704c07c372a4676997
5
+ SHA512:
6
+ metadata.gz: a6afe4b0bf9392cfc4f9335ad283fdf6995b4ffe395274a9e2ecee83a283c0ee358b5db0ffa4f71731e613874b6f395a2a3b586696f53d17c2b8fe94ed673719
7
+ data.tar.gz: 560ddef2c20ed4ff91388e2ede1353c4f2b0b4022478cb7389ec5dc49a28b81dc03b4f68c00381b35fb53c0e07f02da51c7282da4403260e2d0ef4af9dcdefa2
data/GITHUB.md ADDED
@@ -0,0 +1,127 @@
1
+ # GitHub Setup for state_sync
2
+
3
+ `state_sync` reads YAML files directly from a GitHub repository using the GitHub Contents API.
4
+
5
+ ---
6
+
7
+ ## Public repositories
8
+
9
+ No token is required. You can omit `config.token` entirely.
10
+ However, unauthenticated requests are rate-limited to **60 requests/hour** per IP.
11
+ If you enable `auto_refresh` with a short interval, you will hit this limit quickly.
12
+ It is recommended to always provide a token even for public repos (authenticated limit is 5,000/hour).
13
+
14
+ ---
15
+
16
+ ## Private repositories
17
+
18
+ A GitHub token with read access to the repository is required.
19
+
20
+ ---
21
+
22
+ ## Creating a token
23
+
24
+ ### Option A — Fine-grained Personal Access Token (recommended)
25
+
26
+ Fine-grained tokens let you limit access to specific repositories and specific permissions.
27
+
28
+ 1. Go to **GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens**
29
+ 2. Click **Generate new token**
30
+ 3. Set a name (e.g. `state_sync`) and an expiration
31
+ 4. Under **Repository access**, select **Only select repositories** and pick the repo that holds your YAML files
32
+ 5. Under **Permissions → Repository permissions**, find **Contents** and set it to **Read-only**
33
+ 6. Click **Generate token** and copy it immediately — GitHub will not show it again
34
+
35
+ ### Option B — Classic Personal Access Token
36
+
37
+ 1. Go to **GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)**
38
+ 2. Click **Generate new token (classic)**
39
+ 3. Set a note and expiration
40
+ 4. Select the **`repo`** scope (grants full read/write to private repos — use fine-grained tokens if you want narrower access)
41
+ 5. Click **Generate token** and copy it
42
+
43
+ ---
44
+
45
+ ## Storing the token safely
46
+
47
+ Never hardcode a token in your source code. Use an environment variable:
48
+
49
+ ```bash
50
+ # .env (not committed to git)
51
+ GITHUB_TOKEN=ghp_your_token_here
52
+ ```
53
+
54
+ Then reference it in your configuration:
55
+
56
+ ```ruby
57
+ StateSync.configure do |config|
58
+ config.provider = :github
59
+ config.token = ENV["GITHUB_TOKEN"]
60
+ config.repo = "your-org/your-repo"
61
+ end
62
+ ```
63
+
64
+ If you use Rails credentials, you can also store it there:
65
+
66
+ ```bash
67
+ rails credentials:edit
68
+ ```
69
+
70
+ ```yaml
71
+ github:
72
+ state_sync_token: ghp_your_token_here
73
+ ```
74
+
75
+ ```ruby
76
+ config.token = Rails.application.credentials.dig(:github, :state_sync_token)
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Rate limits
82
+
83
+ | Authentication | Requests per hour |
84
+ |-----------------------|-------------------|
85
+ | None (public repos) | 60 |
86
+ | Personal Access Token | 5,000 |
87
+
88
+ If you enable `auto_refresh` with `auto_refresh_interval: 60` (every minute) and have 10 files loaded,
89
+ that is 600 requests/hour — well within the authenticated limit but it will exceed the unauthenticated limit.
90
+ Always use a token when `auto_refresh` is enabled.
91
+
92
+ ---
93
+
94
+ ## Examples
95
+
96
+ ### Without auto refresh
97
+
98
+ Data is fetched once when the server starts. It does not change until the server restarts.
99
+
100
+ ```ruby
101
+ StateSync.configure do |config|
102
+ config.provider = :github
103
+ config.repo = "your-org/your-repo"
104
+ config.token = ENV["GITHUB_TOKEN"]
105
+ config.auto_refresh = false
106
+ end
107
+
108
+ customers = StateSync.load("config/customers.yml")
109
+ customers["customer_ids"] # => [1001, 1002, 1003]
110
+ ```
111
+
112
+ ### With auto refresh
113
+
114
+ Data is fetched at startup and a background thread keeps it updated at the configured interval.
115
+
116
+ ```ruby
117
+ StateSync.configure do |config|
118
+ config.provider = :github
119
+ config.repo = "your-org/your-repo"
120
+ config.token = ENV["GITHUB_TOKEN"]
121
+ config.auto_refresh = true
122
+ config.auto_refresh_interval = 300 # seconds
123
+ end
124
+
125
+ customers = StateSync.load("config/customers.yml")
126
+ customers["customer_ids"] # => always current
127
+ ```
data/GITLAB.md ADDED
@@ -0,0 +1,106 @@
1
+ # GitLab Setup for state_sync
2
+
3
+ `state_sync` reads YAML files directly from a GitLab project using the GitLab Repository Files API.
4
+
5
+ ---
6
+
7
+ ## Public projects
8
+
9
+ No token is required for public projects. You can omit `config.token` entirely.
10
+ A token is still recommended to avoid hitting rate limits, especially when `auto_refresh` is enabled.
11
+
12
+ ---
13
+
14
+ ## Private projects
15
+
16
+ A GitLab personal access token with `read_repository` scope is required.
17
+
18
+ ---
19
+
20
+ ## Creating a token
21
+
22
+ 1. Go to **GitLab → Edit profile → Access tokens**
23
+ 2. Click **Add new token**
24
+ 3. Give it a name (e.g. `state_sync`) and set an expiry date
25
+ 4. Under **Select scopes**, check **`read_repository`**
26
+ 5. Click **Create personal access token** and copy it immediately — GitLab will not show it again
27
+
28
+ ---
29
+
30
+ ## Storing the token safely
31
+
32
+ Never hardcode a token in your source code. Use an environment variable:
33
+
34
+ ```bash
35
+ # .env (not committed to git)
36
+ GITLAB_TOKEN=glpat_your_token_here
37
+ ```
38
+
39
+ Then reference it in your configuration:
40
+
41
+ ```ruby
42
+ StateSync.configure do |config|
43
+ config.provider = :gitlab
44
+ config.token = ENV["GITLAB_TOKEN"]
45
+ config.repo = "your-org/your-repo"
46
+ end
47
+ ```
48
+
49
+ If you use Rails credentials, you can also store it there:
50
+
51
+ ```bash
52
+ rails credentials:edit
53
+ ```
54
+
55
+ ```yaml
56
+ gitlab:
57
+ state_sync_token: glpat_your_token_here
58
+ ```
59
+
60
+ ```ruby
61
+ config.token = Rails.application.credentials.dig(:gitlab, :state_sync_token)
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Rate limits
67
+
68
+ GitLab rate limits vary by plan and instance. Files over 10 MB are limited to 5 requests/minute.
69
+ Always use a token when `auto_refresh` is enabled.
70
+
71
+ ---
72
+
73
+ ## Examples
74
+
75
+ ### Without auto refresh
76
+
77
+ Data is fetched once when the server starts. It does not change until the server restarts.
78
+
79
+ ```ruby
80
+ StateSync.configure do |config|
81
+ config.provider = :gitlab
82
+ config.repo = "your-org/your-repo"
83
+ config.token = ENV["GITLAB_TOKEN"]
84
+ config.auto_refresh = false
85
+ end
86
+
87
+ customers = StateSync.load("config/customers.yml")
88
+ customers["customer_ids"] # => [1001, 1002, 1003]
89
+ ```
90
+
91
+ ### With auto refresh
92
+
93
+ Data is fetched at startup and a background thread keeps it updated at the configured interval.
94
+
95
+ ```ruby
96
+ StateSync.configure do |config|
97
+ config.provider = :gitlab
98
+ config.repo = "your-org/your-repo"
99
+ config.token = ENV["GITLAB_TOKEN"]
100
+ config.auto_refresh = true
101
+ config.auto_refresh_interval = 300 # seconds
102
+ end
103
+
104
+ customers = StateSync.load("config/customers.yml")
105
+ customers["customer_ids"] # => always current
106
+ ```
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # state_sync
2
+
3
+ A Ruby gem for fetching YAML-based feature flags and configuration from a GitHub or GitLab repository.
4
+ Values are loaded at startup and can optionally be kept fresh in the background.
5
+
6
+ ## Installation
7
+
8
+ Add to your Gemfile:
9
+
10
+ ```ruby
11
+ gem "state_sync"
12
+ ```
13
+
14
+ Or install directly:
15
+
16
+ ```bash
17
+ gem install state_sync
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Configuration
23
+
24
+ Token setup: [GITHUB.md](GITHUB.md) | [GITLAB.md](GITLAB.md)
25
+
26
+ ```ruby
27
+ # config/initializers/state_sync.rb
28
+ StateSync.configure do |config|
29
+ config.provider = :github # :github or :gitlab
30
+ config.repo = "your-org/your-repo" # "owner/repo" on GitHub or GitLab
31
+ config.token = ENV["GITHUB_TOKEN"] # optional for public repos
32
+ config.auto_refresh = false # true or false (default: false)
33
+ config.auto_refresh_interval = 300 # seconds, only required when auto_refresh is true
34
+ end
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Usage
40
+
41
+ ```ruby
42
+ # Load a YAML file — fetches immediately on this line
43
+ CUSTOMERS = StateSync.load("config/customers.yml")
44
+
45
+ # Access data
46
+ CUSTOMERS.data # => {"customer_ids" => [1001, 1002, 1003], "feature_x" => true}
47
+ CUSTOMERS["customer_ids"] # => [1001, 1002, 1003]
48
+ CUSTOMERS["feature_x"] # => true
49
+
50
+ # Force a manual refresh at any time
51
+ CUSTOMERS.reload!
52
+ ```
53
+
54
+ ### Example YAML file
55
+
56
+ ```yaml
57
+ customer_ids:
58
+ - 1001
59
+ - 1002
60
+ - 1003
61
+
62
+ feature_x: true
63
+ ```
64
+
65
+ ### Loading multiple files
66
+
67
+ You can load as many files as you need — each gets its own store with independent data and refresh cycle. Define them in your initializer and use them anywhere in your app:
68
+
69
+ ```ruby
70
+ # config/initializers/state_sync.rb
71
+ StateSync.configure do |config|
72
+ config.provider = :github
73
+ config.repo = "your-org/your-repo" # "owner/repo" on GitHub or GitLab
74
+ config.token = ENV["GITHUB_TOKEN"]
75
+ end
76
+
77
+ CUSTOMERS = StateSync.load("config/customers.yml")
78
+ FEATURE_FLAGS = StateSync.load("config/feature_flags.yml")
79
+ PAYMENT_METHODS = StateSync.load("config/payment_methods.yml")
80
+ PAYMENT_LIMITS = StateSync.load("config/payment_limits.yml")
81
+ ```
82
+
83
+ ```ruby
84
+ # Use anywhere in your app
85
+ CUSTOMERS["customer_ids"]
86
+ FEATURE_FLAGS["new_checkout_flow"]
87
+ PAYMENT_METHODS["enabled"]
88
+ PAYMENT_LIMITS["daily_limit"]
89
+ ```
90
+ ---
91
+
92
+ ## Error handling
93
+
94
+ ```ruby
95
+ begin
96
+ flags = StateSync.load("config/flags.yml")
97
+ rescue StateSync::ConfigurationError => e
98
+ # Missing or invalid configuration
99
+ rescue StateSync::FetchError => e
100
+ # Network error, bad token, file not found, etc.
101
+ end
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Try it in IRB
107
+
108
+ First install the gem:
109
+
110
+ ```bash
111
+ gem install state_sync
112
+ ```
113
+
114
+ Then start IRB and require the gem:
115
+
116
+ ```bash
117
+ irb
118
+ ```
119
+
120
+ ```ruby
121
+ require "state_sync"
122
+
123
+ StateSync.configure do |config|
124
+ config.provider = :github
125
+ config.repo = "spmarisa/state_sync_data" # public repo, no token needed
126
+ end
127
+
128
+ customers = StateSync.load("allowed_customers.yml")
129
+ customers.data
130
+ customers["customer_ids"]
131
+
132
+ # Force a refresh
133
+ customers.reload!
134
+ ```
@@ -0,0 +1,23 @@
1
+ class StateSync::Configuration
2
+ PROVIDERS = %i[github gitlab].freeze
3
+
4
+ attr_accessor :provider, :repo, :token, :auto_refresh, :auto_refresh_interval
5
+
6
+ def initialize
7
+ @provider = :github
8
+ @auto_refresh = false
9
+ @auto_refresh_interval = 300
10
+ end
11
+
12
+ def validate!
13
+ unless PROVIDERS.include?(provider)
14
+ raise StateSync::ConfigurationError, "provider must be :github or :gitlab"
15
+ end
16
+
17
+ raise StateSync::ConfigurationError, "repo must be set (e.g. \"owner/repo\")" if repo.nil? || repo.strip.empty?
18
+
19
+ if auto_refresh && (auto_refresh_interval.nil? || auto_refresh_interval <= 0)
20
+ raise StateSync::ConfigurationError, "auto_refresh_interval must be a positive number of seconds when auto_refresh is true"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ class StateSync::Error < StandardError; end
2
+ class StateSync::ConfigurationError < StateSync::Error; end
3
+ class StateSync::FetchError < StateSync::Error; end
@@ -0,0 +1,52 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require "json"
4
+ require "base64"
5
+
6
+ class StateSync::GithubFetcher
7
+ API_BASE = "https://api.github.com"
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ # Fetches raw file content (string) from GitHub.
14
+ # No ?ref= param — GitHub uses the repo's default branch automatically.
15
+ def fetch(path)
16
+ uri = URI("#{API_BASE}/repos/#{@config.repo}/contents/#{path}")
17
+
18
+ request = Net::HTTP::Get.new(uri)
19
+ request["Accept"] = "application/vnd.github+json"
20
+ request["X-GitHub-Api-Version"] = "2022-11-28"
21
+ request["Authorization"] = "Bearer #{@config.token}" if @config.token
22
+
23
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
24
+ http.request(request)
25
+ end
26
+
27
+ handle_response(response, path)
28
+ end
29
+
30
+ private
31
+
32
+ def handle_response(response, path)
33
+ case response.code.to_i
34
+ when 200
35
+ json = JSON.parse(response.body)
36
+
37
+ if json["encoding"] == "base64"
38
+ Base64.decode64(json["content"])
39
+ else
40
+ raise StateSync::FetchError, "Unexpected encoding from GitHub: #{json["encoding"]}"
41
+ end
42
+ when 401
43
+ raise StateSync::FetchError, "GitHub authentication failed — check your github_token."
44
+ when 403
45
+ raise StateSync::FetchError, "GitHub access forbidden — ensure your token has 'Contents' read permission."
46
+ when 404
47
+ raise StateSync::FetchError, "File not found: '#{path}' in repo '#{@config.repo}'. Check the path and that the repo exists."
48
+ else
49
+ raise StateSync::FetchError, "GitHub API returned #{response.code}: #{response.body}"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ require "net/http"
2
+ require "uri"
3
+
4
+ class StateSync::GitlabFetcher
5
+ API_BASE = "https://gitlab.com/api/v4"
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ # Fetches raw file content from GitLab using the repository files raw endpoint.
12
+ # ref=HEAD always resolves to the project's default branch.
13
+ def fetch(path)
14
+ encoded_repo = URI.encode_www_form_component(@config.repo)
15
+ encoded_path = URI.encode_www_form_component(path)
16
+ uri = URI("#{API_BASE}/projects/#{encoded_repo}/repository/files/#{encoded_path}/raw?ref=HEAD")
17
+
18
+ request = Net::HTTP::Get.new(uri)
19
+ request["PRIVATE-TOKEN"] = @config.token if @config.token
20
+
21
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
22
+ http.request(request)
23
+ end
24
+
25
+ handle_response(response, path)
26
+ end
27
+
28
+ private
29
+
30
+ def handle_response(response, path)
31
+ case response.code.to_i
32
+ when 200
33
+ response.body
34
+ when 401
35
+ raise StateSync::FetchError, "GitLab authentication failed — check your gitlab_token."
36
+ when 403
37
+ raise StateSync::FetchError, "GitLab access forbidden — ensure your token has 'read_repository' scope."
38
+ when 404
39
+ raise StateSync::FetchError, "File not found: '#{path}' in repo '#{@config.repo}'. Check the path and that the project exists."
40
+ else
41
+ raise StateSync::FetchError, "GitLab API returned #{response.code}: #{response.body}"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,63 @@
1
+ require "yaml"
2
+
3
+ # Holds the parsed contents of a single YAML file fetched from GitHub or GitLab.
4
+ # On initialization it fetches the file immediately. If auto_refresh is
5
+ # enabled a background thread keeps the data current at the configured interval.
6
+ class StateSync::Store
7
+ def initialize(path)
8
+ @path = path
9
+ @fetcher = fetcher_for(StateSync.configuration)
10
+ @mutex = Mutex.new
11
+
12
+ fetch_and_cache
13
+
14
+ start_background_refresh if StateSync.configuration.auto_refresh
15
+ end
16
+
17
+ # Returns the parsed YAML data (Hash or Array depending on file content).
18
+ def data
19
+ @mutex.synchronize { @data }
20
+ end
21
+
22
+ # Shorthand key access when the YAML root is a Hash.
23
+ def [](key)
24
+ @mutex.synchronize { @data[key] }
25
+ end
26
+
27
+ # Force an immediate re-fetch from the configured provider.
28
+ def reload!
29
+ fetch_and_cache
30
+ self
31
+ end
32
+
33
+ private
34
+
35
+ def fetcher_for(config)
36
+ case config.provider
37
+ when :github then StateSync::GithubFetcher.new(config)
38
+ when :gitlab then StateSync::GitlabFetcher.new(config)
39
+ end
40
+ end
41
+
42
+ def fetch_and_cache
43
+ content = @fetcher.fetch(@path)
44
+ parsed = YAML.safe_load(content)
45
+ @mutex.synchronize { @data = parsed }
46
+ end
47
+
48
+ def start_background_refresh
49
+ interval = StateSync.configuration.auto_refresh_interval
50
+
51
+ Thread.new do
52
+ Thread.current.daemon = true
53
+ loop do
54
+ sleep interval
55
+ begin
56
+ fetch_and_cache
57
+ rescue => e
58
+ warn "[StateSync] Failed to refresh '#{@path}': #{e.message}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module StateSync
2
+ VERSION = "0.1.0"
3
+ end
data/lib/state_sync.rb ADDED
@@ -0,0 +1,29 @@
1
+ require "state_sync/version"
2
+ require "state_sync/errors"
3
+ require "state_sync/configuration"
4
+ require "state_sync/store"
5
+ Dir[File.join(__dir__, "state_sync/fetchers/*.rb")].each { |f| require f }
6
+
7
+ module StateSync
8
+ class << self
9
+ def configure
10
+ yield configuration
11
+ configuration.validate!
12
+ end
13
+
14
+ def configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ # Loads a YAML file from the configured provider (GitHub or GitLab) and returns a Store.
19
+ # The file is fetched immediately; if auto_refresh is enabled a background
20
+ # thread keeps it updated at the configured interval.
21
+ #
22
+ # Example:
23
+ # customers = StateSync.load("config/customers.yml")
24
+ # customers["allowed_ids"] # => [1, 2, 3]
25
+ def load(path)
26
+ Store.new(path)
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: state_sync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Phaneendra Marisa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: webmock
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.23'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.23'
41
+ description:
42
+ email:
43
+ - phaneendra.marisa@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - GITHUB.md
49
+ - GITLAB.md
50
+ - LICENSE
51
+ - README.md
52
+ - lib/state_sync.rb
53
+ - lib/state_sync/configuration.rb
54
+ - lib/state_sync/errors.rb
55
+ - lib/state_sync/fetchers/github_fetcher.rb
56
+ - lib/state_sync/fetchers/gitlab_fetcher.rb
57
+ - lib/state_sync/store.rb
58
+ - lib/state_sync/version.rb
59
+ homepage: https://github.com/spmarisa/state_sync
60
+ licenses:
61
+ - WTFPL
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 2.5.0
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.0.3.1
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Fetch and auto-refresh YAML-based feature flags and config from a Git repository.
82
+ test_files: []