release_feature 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 +7 -0
- data/.github/workflows/main.yml +23 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +17 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +151 -0
- data/LICENSE.txt +21 -0
- data/README.md +264 -0
- data/Rakefile +12 -0
- data/Steepfile +9 -0
- data/bin/console +15 -0
- data/bin/setup +9 -0
- data/lib/release_feature/client.rb +83 -0
- data/lib/release_feature/example/config.yml +20 -0
- data/lib/release_feature/feature.rb +117 -0
- data/lib/release_feature/feature_collection.rb +51 -0
- data/lib/release_feature/repository/active_record_repository.rb +46 -0
- data/lib/release_feature/repository/hash_repository.rb +45 -0
- data/lib/release_feature/repository/yaml_repository.rb +90 -0
- data/lib/release_feature/version.rb +5 -0
- data/lib/release_feature.rb +15 -0
- data/rbs_collection.lock.yaml +104 -0
- data/rbs_collection.yaml +18 -0
- data/release_feature.gemspec +40 -0
- data/sig/lib/release_feature/client.rbs +31 -0
- data/sig/lib/release_feature/feature.rbs +47 -0
- data/sig/lib/release_feature/feature_collection.rbs +15 -0
- data/sig/lib/release_feature/repository/active_record_repository.rbs +17 -0
- data/sig/lib/release_feature/repository/hash_repository.rbs +18 -0
- data/sig/lib/release_feature/repository/yaml_repository.rbs +33 -0
- data/sig/lib/release_feature/version.rbs +3 -0
- data/sig/lib/release_feature.rbs +4 -0
- metadata +249 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReleaseFeature
|
4
|
+
class Client
|
5
|
+
attr_reader :updated_at
|
6
|
+
|
7
|
+
# @param [String, Symbol] environment
|
8
|
+
# @param [ActiveRecordRepository, YamlRecordRepository, HashRecordRepository] base_repository
|
9
|
+
# @param [nil] extra_repository
|
10
|
+
# @param [Integer] refresh_interval_sec
|
11
|
+
def initialize(environment:, base_repository:, extra_repository: nil, refresh_interval_sec: 600)
|
12
|
+
@environment = environment.to_sym
|
13
|
+
@base_repository = base_repository
|
14
|
+
@extra_repository = extra_repository
|
15
|
+
@refresh_interval_sec = if refresh_interval_sec.is_a?(Integer)
|
16
|
+
refresh_interval_sec
|
17
|
+
else
|
18
|
+
raise ReleaseFeature::Error, 'refresh_interval_sec must be Integer'
|
19
|
+
end
|
20
|
+
load_features
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [Symbol] feature_name
|
24
|
+
# @param [TrueClass, FalseClass] time
|
25
|
+
def permitted?(feature_name, time: nil)
|
26
|
+
time ||= Time.now
|
27
|
+
|
28
|
+
refresh if expired?(time)
|
29
|
+
|
30
|
+
name = feature_name.to_sym
|
31
|
+
raise ReleaseFeature::Error, "#{feature_name} is not registered." if @features[feature_name.to_sym].nil?
|
32
|
+
|
33
|
+
feature = to_feature(@features, name, @environment)
|
34
|
+
return false if feature.nil?
|
35
|
+
|
36
|
+
feature.permitted?(time)
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_features
|
40
|
+
@features
|
41
|
+
end
|
42
|
+
|
43
|
+
def refresh
|
44
|
+
load_features
|
45
|
+
true
|
46
|
+
rescue StandardError => e
|
47
|
+
e.message
|
48
|
+
# errorがある場合は@featuresを更新しない
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def names
|
53
|
+
@features.names
|
54
|
+
end
|
55
|
+
|
56
|
+
def environments
|
57
|
+
@features.environments
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def to_feature(features, name, environment)
|
63
|
+
features.dig(name, environment)
|
64
|
+
end
|
65
|
+
|
66
|
+
def expired?(time = nil)
|
67
|
+
time ||= Time.now
|
68
|
+
raise ReleaseFeature::Error, 'current_time is must be Time class' unless time.is_a?(Time)
|
69
|
+
|
70
|
+
@updated_at + @refresh_interval_sec < time
|
71
|
+
end
|
72
|
+
|
73
|
+
def load_features
|
74
|
+
features = if @extra_repository
|
75
|
+
@base_repository.features.combine_features(@extra_repository.features)
|
76
|
+
else
|
77
|
+
@base_repository.features
|
78
|
+
end
|
79
|
+
@features = features
|
80
|
+
@updated_at = Time.now
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
timezone: JST
|
2
|
+
features:
|
3
|
+
hoge_feature:
|
4
|
+
development:
|
5
|
+
open_at: 2000-01-01 00:00
|
6
|
+
close_at: 2999-12-31 23:59
|
7
|
+
staging:
|
8
|
+
open_at: 2021-11-01 10:00
|
9
|
+
close_at: 2999-12-31 23:59
|
10
|
+
production:
|
11
|
+
open_at: 2021-12-01 10:00
|
12
|
+
close_at: 2999-12-31 23:59
|
13
|
+
puyo_feature:
|
14
|
+
development:
|
15
|
+
open_at: 2000-01-01 00:00
|
16
|
+
close_at: 2999-12-31 23:59
|
17
|
+
production:
|
18
|
+
open_at: 2022-12-01 10:00
|
19
|
+
close_at: 2999-12-31 23:59
|
20
|
+
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReleaseFeature
|
4
|
+
class Feature
|
5
|
+
attr_reader :name, :environment, :open_at, :close_at, :errors
|
6
|
+
|
7
|
+
# @param [String, Symbol] name
|
8
|
+
# @param [String, Symbol] environment
|
9
|
+
# @param [Time] open_at
|
10
|
+
# @param [Time] close_at
|
11
|
+
def initialize(name:, environment:, open_at:, close_at:)
|
12
|
+
validate_present({ name: name, environment: environment, open_at: open_at, close_at: close_at })
|
13
|
+
@name = cast_to_sym(:name, name)
|
14
|
+
@environment = cast_to_sym(:environment, environment)
|
15
|
+
@open_at = open_at
|
16
|
+
@close_at = close_at
|
17
|
+
raise ReleaseFeature::Error, errors_full_message unless valid?
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [Time] current_time
|
21
|
+
# @return [TrueClass, FalseClass]
|
22
|
+
def permitted?(current_time)
|
23
|
+
(open_at...close_at).cover?(current_time)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @return [TrueClass, FalseClass]
|
29
|
+
def valid?
|
30
|
+
initialize_errors
|
31
|
+
validate_all
|
32
|
+
@errors.size.zero?
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_present(attrs)
|
36
|
+
attrs.each do |k, v|
|
37
|
+
if v.nil?
|
38
|
+
raise ReleaseFeature::Error,
|
39
|
+
"#{k} must be present."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_all
|
45
|
+
validate_name
|
46
|
+
validate_open_at
|
47
|
+
validate_close_at
|
48
|
+
validate_range
|
49
|
+
end
|
50
|
+
|
51
|
+
def errors_full_message
|
52
|
+
@errors.join(' ')
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize_errors
|
56
|
+
@errors = []
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param [String, Symbol] attr_name
|
60
|
+
# @param [Object] val
|
61
|
+
# @param [String] message
|
62
|
+
def add_error(attr_name, val, message)
|
63
|
+
@errors << "#{val} of #{attr_name} has error. #{message}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_name
|
67
|
+
message = name_error_message(name)
|
68
|
+
return unless message
|
69
|
+
|
70
|
+
add_error('name', name, message)
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate_open_at
|
74
|
+
return if time_present?(open_at)
|
75
|
+
|
76
|
+
add_error('open_at', open_at, 'set time to open_at.')
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate_close_at
|
80
|
+
return if time_present?(close_at)
|
81
|
+
|
82
|
+
add_error('close_at', close_at, 'set time to close_at.')
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_range
|
86
|
+
return if open_at < close_at
|
87
|
+
|
88
|
+
add_error('open_at', open_at, 'open_at is less than close_at.')
|
89
|
+
add_error('close_at', close_at, 'close_at is more than open_at.')
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param [String] attr
|
93
|
+
def time_present?(attr)
|
94
|
+
attr.is_a?(Time)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param [Symbol] key
|
98
|
+
# @param [String, Symbol] str
|
99
|
+
def cast_to_sym(key, str)
|
100
|
+
raise ReleaseFeature::Error, "#{key} must be String or Symbol" unless str.is_a?(String) || str.is_a?(Symbol)
|
101
|
+
|
102
|
+
str.to_sym
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param [String, Symbol] name
|
106
|
+
# @return [String, NilClass]
|
107
|
+
def name_error_message(name)
|
108
|
+
if name.to_s.match?(/\s/)
|
109
|
+
'space is not permitted to name.'
|
110
|
+
elsif name.to_s.size < 8
|
111
|
+
'more than 7 character is permitted to name.'
|
112
|
+
elsif !name.to_s.match?(/^[a-z]([a-z0-9_]{7,})$/)
|
113
|
+
' 0-9 or a-z or _ is permitted to name.'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReleaseFeature
|
4
|
+
class FeatureCollection < Hash
|
5
|
+
# @param [ReleaseFeature::FeatureCollection] features
|
6
|
+
# @return [ReleaseFeature::FeatureCollection]
|
7
|
+
def combine_features(features)
|
8
|
+
all_names = (names + features.names).uniq
|
9
|
+
all_environments = (environments + features.environments).uniq
|
10
|
+
all_names.each_with_object(ReleaseFeature::FeatureCollection.new).each do |name, memo_obj|
|
11
|
+
memo_obj[name] = build_environments(name, all_environments, features)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Array<Symbol>]
|
16
|
+
def names
|
17
|
+
keys
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Array<Symbol>]
|
21
|
+
def environments
|
22
|
+
values.each_with_object([]) { |a, memo| memo.concat(a.keys) }.uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# @param [:Symbol] name
|
28
|
+
# @param [:Symbol] environments
|
29
|
+
# @param [ReleaseFeature::FeatureCollection] features
|
30
|
+
# @return [Hash{Symbol->Hash}]
|
31
|
+
def build_environments(name, environments, features)
|
32
|
+
environments.each_with_object({}) do |environment, memo_obj|
|
33
|
+
next unless build_feature(name, environment, features)
|
34
|
+
|
35
|
+
memo_obj[environment] = build_feature(name, environment, features)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [:Symbol] name
|
40
|
+
# @param [:Symbol] environments
|
41
|
+
# @param [ReleaseFeature::FeatureCollection] features
|
42
|
+
# @return [ReleaseFeature::Feature]
|
43
|
+
def build_feature(name, environment, features)
|
44
|
+
if features.dig(name, environment)
|
45
|
+
features.dig(name, environment)
|
46
|
+
elsif dig(name, environment)
|
47
|
+
dig(name, environment)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReleaseFeature
|
4
|
+
module Repository
|
5
|
+
class ActiveRecordRepository
|
6
|
+
# @param [Class] model
|
7
|
+
def initialize(model:)
|
8
|
+
@model = model
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [ReleaseFeature::FeatureCollection]
|
12
|
+
def features
|
13
|
+
data = @model.all
|
14
|
+
convert_to_feature_collection(data)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# @param [ActiveRecord] data
|
20
|
+
# @return [ReleaseFeature::FeatureCollection]
|
21
|
+
def convert_to_feature_collection(data)
|
22
|
+
data.each_with_object(ReleaseFeature::FeatureCollection.new) do |record, memo_obj|
|
23
|
+
name_key = record.name.to_sym
|
24
|
+
environment_key = record.environment.to_sym
|
25
|
+
feature = build_feature(record)
|
26
|
+
if memo_obj[name_key].nil?
|
27
|
+
memo_obj[name_key] = { environment_key => feature }
|
28
|
+
else
|
29
|
+
memo_obj[name_key][environment_key] = feature
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [ActiveRecord] record
|
35
|
+
# @return [ReleaseFeature::Feature]
|
36
|
+
def build_feature(record)
|
37
|
+
ReleaseFeature::Feature.new(
|
38
|
+
name: record.name,
|
39
|
+
environment: record.environment,
|
40
|
+
open_at: record.open_at,
|
41
|
+
close_at: record.close_at
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReleaseFeature
|
4
|
+
module Repository
|
5
|
+
class HashRepository
|
6
|
+
# @param [Hash] hash
|
7
|
+
def initialize(hash:)
|
8
|
+
@hash = hash
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [ReleaseFeature::FeatureCollection]
|
12
|
+
def features
|
13
|
+
data = @hash
|
14
|
+
convert_to_feature_collection(data)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# @param [Hash] data
|
20
|
+
# @return [ReleaseFeature::FeatureCollection]
|
21
|
+
def convert_to_feature_collection(data)
|
22
|
+
data[:features].each_with_object(ReleaseFeature::FeatureCollection.new) do |(name, environments), memo_obj|
|
23
|
+
memo_obj[name] = {}
|
24
|
+
environments.each do |environment, range|
|
25
|
+
memo_obj[name][environment] = build_feature(name, environment, range[:open_at], range[:close_at])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [Symbol] name
|
31
|
+
# @param [Symbol] environment
|
32
|
+
# @param [Time] open_at
|
33
|
+
# @param [Time] close_at
|
34
|
+
# @return [ReleaseFeature::Feature]
|
35
|
+
def build_feature(name, environment, open_at, close_at)
|
36
|
+
ReleaseFeature::Feature.new(
|
37
|
+
name: name,
|
38
|
+
environment: environment,
|
39
|
+
open_at: open_at,
|
40
|
+
close_at: close_at
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tzinfo'
|
4
|
+
module ReleaseFeature
|
5
|
+
module Repository
|
6
|
+
class YamlRepository
|
7
|
+
class AccessibleRange
|
8
|
+
attr_reader :open_at, :close_at
|
9
|
+
|
10
|
+
def initialize(open_at:, close_at:)
|
11
|
+
@open_at = open_at
|
12
|
+
@close_at = close_at
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [String] path
|
17
|
+
def initialize(path:)
|
18
|
+
@path = path
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [ReleaseFeature::FeatureCollection]
|
22
|
+
def features
|
23
|
+
data = YAML.load_file(@path)
|
24
|
+
convert_to_feature_collection(data)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# @param [Hash] data
|
30
|
+
# @return [ReleaseFeature::FeatureCollection]
|
31
|
+
def convert_to_feature_collection(data)
|
32
|
+
timezone = data['timezone']
|
33
|
+
features = data['features']
|
34
|
+
symbolize_features(features, timezone)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [Hash] features
|
38
|
+
# @return [ReleaseFeature::FeatureCollection]
|
39
|
+
def symbolize_features(features, timezone)
|
40
|
+
features.each_with_object(ReleaseFeature::FeatureCollection.new) do |(name, environments), memo_obj|
|
41
|
+
environments.each do |environment, range|
|
42
|
+
if memo_obj[name.to_sym]
|
43
|
+
memo_obj[name.to_sym][environment.to_sym] = build_feature(name, environment, range, timezone)
|
44
|
+
else
|
45
|
+
memo_obj[name.to_sym] = { environment.to_sym => build_feature(name, environment, range, timezone) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param [Str] name
|
52
|
+
# @param [Str] environment
|
53
|
+
# @param [Hash] range
|
54
|
+
# @param [String] timezone
|
55
|
+
# @return [ReleaseFeature::Feature]
|
56
|
+
def build_feature(name, environment, range, timezone)
|
57
|
+
accessible_range = to_range_struct(range, timezone)
|
58
|
+
ReleaseFeature::Feature.new(
|
59
|
+
name: name.to_sym,
|
60
|
+
environment: environment.to_sym,
|
61
|
+
open_at: accessible_range.open_at,
|
62
|
+
close_at: accessible_range.close_at
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [Hash] range
|
67
|
+
# @param [String] timezone
|
68
|
+
# @return [AccessibleRange]
|
69
|
+
def to_range_struct(range, timezone)
|
70
|
+
AccessibleRange.new(
|
71
|
+
open_at: cast_to_time(range['open_at'], timezone),
|
72
|
+
close_at: cast_to_time(range['close_at'], timezone)
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param [String] str
|
77
|
+
# @param [String] timezone
|
78
|
+
# @return [Time]
|
79
|
+
def cast_to_time(str, timezone)
|
80
|
+
time = Time.parse(str)
|
81
|
+
year = time.year
|
82
|
+
month = time.month
|
83
|
+
day = time.day
|
84
|
+
hour = time.hour
|
85
|
+
minute = time.min
|
86
|
+
Time.new(year, month, day, hour, minute, 0, timezone)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
require 'yaml'
|
5
|
+
require_relative 'release_feature/version'
|
6
|
+
require_relative 'release_feature/repository/yaml_repository'
|
7
|
+
require_relative 'release_feature/repository/active_record_repository'
|
8
|
+
require_relative 'release_feature/repository/hash_repository'
|
9
|
+
require_relative 'release_feature/feature'
|
10
|
+
require_relative 'release_feature/client'
|
11
|
+
require_relative 'release_feature/feature_collection'
|
12
|
+
|
13
|
+
module ReleaseFeature
|
14
|
+
class Error < StandardError; end
|
15
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
---
|
2
|
+
sources:
|
3
|
+
- name: ruby/gem_rbs_collection
|
4
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
5
|
+
revision: main
|
6
|
+
repo_dir: gems
|
7
|
+
path: ".gem_rbs_collection"
|
8
|
+
gems:
|
9
|
+
- name: date
|
10
|
+
version: '0'
|
11
|
+
source:
|
12
|
+
type: stdlib
|
13
|
+
- name: time
|
14
|
+
version: '0'
|
15
|
+
source:
|
16
|
+
type: stdlib
|
17
|
+
- name: activerecord
|
18
|
+
version: '6.1'
|
19
|
+
source:
|
20
|
+
type: git
|
21
|
+
name: ruby/gem_rbs_collection
|
22
|
+
revision: 5e006c8976e661f16b61ba9193acce07f82b4ec5
|
23
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
24
|
+
repo_dir: gems
|
25
|
+
- name: activemodel
|
26
|
+
version: '6.0'
|
27
|
+
source:
|
28
|
+
type: git
|
29
|
+
name: ruby/gem_rbs_collection
|
30
|
+
revision: 5e006c8976e661f16b61ba9193acce07f82b4ec5
|
31
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
32
|
+
repo_dir: gems
|
33
|
+
- name: activesupport
|
34
|
+
version: '6.0'
|
35
|
+
source:
|
36
|
+
type: git
|
37
|
+
name: ruby/gem_rbs_collection
|
38
|
+
revision: 5e006c8976e661f16b61ba9193acce07f82b4ec5
|
39
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
40
|
+
repo_dir: gems
|
41
|
+
- name: ast
|
42
|
+
version: '2.4'
|
43
|
+
source:
|
44
|
+
type: git
|
45
|
+
name: ruby/gem_rbs_collection
|
46
|
+
revision: 5e006c8976e661f16b61ba9193acce07f82b4ec5
|
47
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
48
|
+
repo_dir: gems
|
49
|
+
- name: listen
|
50
|
+
version: '3.2'
|
51
|
+
source:
|
52
|
+
type: git
|
53
|
+
name: ruby/gem_rbs_collection
|
54
|
+
revision: 5e006c8976e661f16b61ba9193acce07f82b4ec5
|
55
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
56
|
+
repo_dir: gems
|
57
|
+
- name: parallel
|
58
|
+
version: '1.20'
|
59
|
+
source:
|
60
|
+
type: git
|
61
|
+
name: ruby/gem_rbs_collection
|
62
|
+
revision: 5e006c8976e661f16b61ba9193acce07f82b4ec5
|
63
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
64
|
+
repo_dir: gems
|
65
|
+
- name: rainbow
|
66
|
+
version: '3.0'
|
67
|
+
source:
|
68
|
+
type: git
|
69
|
+
name: ruby/gem_rbs_collection
|
70
|
+
revision: 5e006c8976e661f16b61ba9193acce07f82b4ec5
|
71
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
72
|
+
repo_dir: gems
|
73
|
+
- name: yaml
|
74
|
+
version: '0'
|
75
|
+
source:
|
76
|
+
type: stdlib
|
77
|
+
- name: monitor
|
78
|
+
version: '0'
|
79
|
+
source:
|
80
|
+
type: stdlib
|
81
|
+
- name: singleton
|
82
|
+
version: '0'
|
83
|
+
source:
|
84
|
+
type: stdlib
|
85
|
+
- name: logger
|
86
|
+
version: '0'
|
87
|
+
source:
|
88
|
+
type: stdlib
|
89
|
+
- name: mutex_m
|
90
|
+
version: '0'
|
91
|
+
source:
|
92
|
+
type: stdlib
|
93
|
+
- name: pathname
|
94
|
+
version: '0'
|
95
|
+
source:
|
96
|
+
type: stdlib
|
97
|
+
- name: dbm
|
98
|
+
version: '0'
|
99
|
+
source:
|
100
|
+
type: stdlib
|
101
|
+
- name: pstore
|
102
|
+
version: '0'
|
103
|
+
source:
|
104
|
+
type: stdlib
|
data/rbs_collection.yaml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Download sources
|
2
|
+
sources:
|
3
|
+
- name: ruby/gem_rbs_collection
|
4
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
5
|
+
revision: main
|
6
|
+
repo_dir: gems
|
7
|
+
|
8
|
+
# A directory to install the downloaded RBSs
|
9
|
+
path: .gem_rbs_collection
|
10
|
+
|
11
|
+
gems:
|
12
|
+
# Skip loading rbs gem's RBS.
|
13
|
+
# It's unnecessary if you don't use rbs as a library.
|
14
|
+
- name: rbs
|
15
|
+
ignore: true
|
16
|
+
- name: date
|
17
|
+
- name: time
|
18
|
+
- name: activerecord
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/release_feature/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'release_feature'
|
7
|
+
spec.version = ReleaseFeature::VERSION
|
8
|
+
spec.authors = ['junara']
|
9
|
+
spec.email = ['jun5araki@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Release feature in custom date range.'
|
12
|
+
spec.description = 'Release feature in custom date range.'
|
13
|
+
spec.homepage = 'https://github.com/junara/release_feature'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
|
16
|
+
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/junara/release_feature/blob/main/CHANGELOG.md'
|
20
|
+
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = 'exe'
|
25
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ['lib']
|
27
|
+
spec.add_development_dependency 'activerecord'
|
28
|
+
spec.add_development_dependency 'factory_bot'
|
29
|
+
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'rspec-parameterized'
|
31
|
+
spec.add_development_dependency 'rubocop'
|
32
|
+
spec.add_development_dependency 'rubocop-performance'
|
33
|
+
spec.add_development_dependency 'rubocop-rake'
|
34
|
+
spec.add_development_dependency 'rubocop-rspec'
|
35
|
+
spec.add_development_dependency 'sqlite3'
|
36
|
+
spec.add_development_dependency 'timecop'
|
37
|
+
spec.add_dependency 'time'
|
38
|
+
spec.add_dependency 'yaml'
|
39
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ReleaseFeature
|
2
|
+
class Client
|
3
|
+
@environment: Symbol
|
4
|
+
@base_repository: Repository::YamlRepository | Repository::HashRepository | Repository::ActiveRecordRepository
|
5
|
+
@extra_repository: untyped | Repository::YamlRepository | Repository::HashRepository | Repository::ActiveRecordRepository
|
6
|
+
@refresh_interval_sec: Integer
|
7
|
+
@features: FeatureCollection
|
8
|
+
|
9
|
+
attr_reader updated_at: Time
|
10
|
+
|
11
|
+
def initialize: (environment: String | Symbol, base_repository: Repository::YamlRepository | Repository::HashRepository | Repository::ActiveRecordRepository, ?extra_repository: Repository::YamlRepository | Repository::HashRepository | Repository::ActiveRecordRepository, ?refresh_interval_sec: Integer) -> void
|
12
|
+
|
13
|
+
def permitted?: (Symbol feature_name, ?time: nil | Time) -> bool
|
14
|
+
|
15
|
+
def current_features: -> FeatureCollection
|
16
|
+
|
17
|
+
def refresh: -> bool
|
18
|
+
|
19
|
+
def names: -> Array[Symbol]
|
20
|
+
|
21
|
+
def environments: -> Array[Symbol]
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def to_feature: (FeatureCollection features, Symbol name, Symbol environment) -> Feature
|
26
|
+
|
27
|
+
def expired?: (?Time? time) -> bool
|
28
|
+
|
29
|
+
def load_features: -> Time
|
30
|
+
end
|
31
|
+
end
|