code_teams 1.0.2 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 443aa599a4db945ed8020950a938cdaad99818c52728883115b72cd5eda5c68a
4
- data.tar.gz: 298e59f227cc4a07dd64fd2bc03dde9b57e46965dd7eb5be9ce72153928631a3
3
+ metadata.gz: 755f17a56df0e3c1f337e0497b4b63887b0681626583bdf3a6f66ec4eb9ce46f
4
+ data.tar.gz: e060b12412a3a5c0ff02af18490a3ef336b8b83d5236ef5eed0f4a75c12ced2b
5
5
  SHA512:
6
- metadata.gz: 744e2d89ba032674399ca1f449b5f139cc11fb9818e6411346c638c12232f727971e46500f4acbad832412299612f1ef3b3fbc38a6e77a582e1ea2779cbd3b73
7
- data.tar.gz: ade5869ed1e8b5d4f5768b61c4c66df84ef0c6f0bd9190dbedbafabe9c0d8bfdd2e1ff83f3d83d6310934575ded45e274955651d9591c7fcf4fccbadea37fede
6
+ metadata.gz: 92594da3e4eccae671cd71d1564daf10009d41bd30612a0d5c1b66e3a729466d2c03ccea4c5cad5cf2196bc85be33a06520ec4767e94bb75219f070c8088449f
7
+ data.tar.gz: 9acbb9c011703218fe398179e723c796452b4564a06817124fd7cd599ea9e93a6d7f01987e0f03f274ddd100074d5d8260a18a85d51da69ae15a458bd992e29b
data/README.md CHANGED
@@ -30,10 +30,7 @@ class MyGithubPlugin < CodeTeams::Plugin
30
30
  end
31
31
 
32
32
  def member?(user)
33
- members = github.members
34
- return false unless members
35
-
36
- members.include?(user)
33
+ github.members.include?(user)
37
34
  end
38
35
 
39
36
  sig { override.params(teams: T::Array[CodeTeams::Team]).returns(T::Array[String]) }
@@ -62,24 +59,50 @@ github:
62
59
  ```
63
60
 
64
61
  1) You can now use the following API to get GitHub information about that team:
65
- ```ruby
66
- team = CodeTeams.find('My Team')
67
- MyGithubPlugin.for(team).github
68
- ```
62
+
63
+ ```ruby
64
+ team = CodeTeams.find('My Team')
65
+ members = team.github.members
66
+ github_name = team.github.team
67
+ ```
68
+
69
+ Alternatively, you can assign an accessor method name that differs from the plugin's class name:
70
+
71
+ ```ruby
72
+ class MyPlugin < CodeTeams::Plugin
73
+ data_accessor_name :other_name
74
+
75
+ def other_name
76
+ # ...
77
+ end
78
+ end
79
+
80
+ # You can then use:
81
+ team.other_name
82
+ # similarly to the Github example above
83
+ # You can then access data in the following manner:
84
+ team.other_name.attribute_name
85
+ ```
86
+
87
+ However, to avoid confusion, it's recommended to use the naming convention
88
+ whenever possible so that your accessor name matches your plugin's name
89
+
69
90
  2) Running team validations (see below) will ensure all teams have a GitHub team specified
70
91
 
71
- Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for:
72
- - Identifying which teams own which feature flags
73
- - Mapping teams to specific portions of the code through `code_ownership`
74
- - Allowing teams to protect certain files and require approval on modification of certain files
75
- - Specifying owned dependencies (Ruby gems, JavaScript packages, and more)
76
- - Specifying how to get in touch with the team via Slack (their channel and handle)
92
+ Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for:
93
+
94
+ - Identifying which teams own which feature flags
95
+ - Mapping teams to specific portions of the code through `code_ownership`
96
+ - Allowing teams to protect certain files and require approval on modification of certain files
97
+ - Specifying owned dependencies (Ruby gems, JavaScript packages, and more)
98
+ - Specifying how to get in touch with the team via Slack (their channel and handle)
77
99
 
78
100
  ## Configuration
79
101
  You'll want to ensure that all teams are valid in your CI environment. We recommend running code like this in CI:
80
102
  ```ruby
81
103
  require 'code_teams'
82
- errors = ::CodeTeams.validation_errors(::CodeTeams.all)
104
+
105
+ errors = CodeTeams.validation_errors(CodeTeams.all)
83
106
  if errors.any?
84
107
  abort <<~ERROR
85
108
  Team validation failed with the following errors:
@@ -10,11 +10,24 @@ module CodeTeams
10
10
 
11
11
  abstract!
12
12
 
13
+ @data_accessor_name = T.let(nil, T.nilable(String))
14
+
13
15
  sig { params(team: Team).void }
14
16
  def initialize(team)
15
17
  @team = team
16
18
  end
17
19
 
20
+ sig { params(key: String).returns(String) }
21
+ def self.data_accessor_name(key = default_data_accessor_name)
22
+ @data_accessor_name ||= key
23
+ end
24
+
25
+ sig { returns(String) }
26
+ def self.default_data_accessor_name
27
+ # e.g., MyNamespace::MyPlugin -> my_plugin
28
+ Utils.underscore(Utils.demodulize(name))
29
+ end
30
+
18
31
  sig { params(base: T.untyped).void }
19
32
  def self.inherited(base) # rubocop:disable Lint/MissingSuper
20
33
  all_plugins << T.cast(base, T.class_of(Plugin))
@@ -28,7 +41,7 @@ module CodeTeams
28
41
  end
29
42
 
30
43
  sig { params(teams: T::Array[Team]).returns(T::Array[String]) }
31
- def self.validation_errors(teams) # rubocop:disable Lint/UnusedMethodArgument
44
+ def self.validation_errors(teams)
32
45
  []
33
46
  end
34
47
 
@@ -42,9 +55,9 @@ module CodeTeams
42
55
  "#{team.name} is missing required key `#{key}`"
43
56
  end
44
57
 
45
- sig { returns(T::Hash[T.nilable(String), T::Hash[Class, Plugin]]) }
58
+ sig { returns(T::Hash[T.nilable(String), T::Hash[T.class_of(Plugin), Plugin]]) }
46
59
  def self.registry
47
- @registry ||= T.let(@registry, T.nilable(T::Hash[String, T::Hash[Class, Plugin]]))
60
+ @registry ||= T.let(@registry, T.nilable(T::Hash[String, T::Hash[T.class_of(Plugin), Plugin]]))
48
61
  @registry ||= {}
49
62
  @registry
50
63
  end
@@ -0,0 +1,17 @@
1
+ module CodeTeams
2
+ module Utils
3
+ module_function
4
+
5
+ def underscore(string)
6
+ string.gsub('::', '/')
7
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
8
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
9
+ .tr('-', '_')
10
+ .downcase
11
+ end
12
+
13
+ def demodulize(string)
14
+ string.split('::').last
15
+ end
16
+ end
17
+ end
data/lib/code_teams.rb CHANGED
@@ -3,11 +3,11 @@
3
3
  # typed: strict
4
4
 
5
5
  require 'yaml'
6
- require 'set'
7
6
  require 'pathname'
8
7
  require 'sorbet-runtime'
9
8
  require 'code_teams/plugin'
10
9
  require 'code_teams/plugins/identity'
10
+ require 'code_teams/utils'
11
11
 
12
12
  module CodeTeams
13
13
  extend T::Sig
@@ -15,6 +15,7 @@ module CodeTeams
15
15
  class IncorrectPublicApiUsageError < StandardError; end
16
16
 
17
17
  UNKNOWN_TEAM_STRING = 'Unknown Team'
18
+ @plugins_registered = T.let(false, T::Boolean)
18
19
 
19
20
  sig { returns(T::Array[Team]) }
20
21
  def self.all
@@ -36,6 +37,11 @@ module CodeTeams
36
37
 
37
38
  sig { params(dir: String).returns(T::Array[Team]) }
38
39
  def self.for_directory(dir)
40
+ unless @plugins_registered
41
+ Team.register_plugins
42
+ @plugins_registered = true
43
+ end
44
+
39
45
  Pathname.new(dir).glob('**/*.yml').map do |path|
40
46
  Team.from_yml(path.to_s)
41
47
  rescue Psych::SyntaxError
@@ -60,6 +66,7 @@ module CodeTeams
60
66
  # The primary reason this is helpful is for clients of CodeTeams who want to test their code, and each test context has different set of teams
61
67
  sig { void }
62
68
  def self.bust_caches!
69
+ @plugins_registered = false
63
70
  Plugin.bust_caches!
64
71
  @all = nil
65
72
  @index_by_name = nil
@@ -86,6 +93,17 @@ module CodeTeams
86
93
  )
87
94
  end
88
95
 
96
+ sig { void }
97
+ def self.register_plugins
98
+ Plugin.all_plugins.each do |plugin|
99
+ # e.g., def github (on Team)
100
+ define_method(plugin.data_accessor_name) do
101
+ # e.g., MyGithubPlugin.for(team).github
102
+ plugin.for(T.cast(self, Team)).public_send(plugin.data_accessor_name)
103
+ end
104
+ end
105
+ end
106
+
89
107
  sig { returns(T::Hash[T.untyped, T.untyped]) }
90
108
  attr_reader :raw_hash
91
109
 
@@ -116,16 +134,16 @@ module CodeTeams
116
134
  sig { params(other: Object).returns(T::Boolean) }
117
135
  def ==(other)
118
136
  if other.is_a?(CodeTeams::Team)
119
- self.name == other.name
137
+ name == other.name
120
138
  else
121
139
  false
122
140
  end
123
141
  end
124
142
 
125
- alias_method :eql?, :==
143
+ alias eql? ==
126
144
 
127
145
  sig { returns(Integer) }
128
- def hash # rubocop:disable Rails/Delegate
146
+ def hash
129
147
  name.hash
130
148
  end
131
149
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code_teams
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-07-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: sorbet-runtime
@@ -24,62 +23,6 @@ dependencies:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: pry
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rspec
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '3.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: sorbet
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
26
  description: A low-dependency gem for declaring and querying engineering teams
84
27
  email:
85
28
  - dev@gusto.com
@@ -91,6 +34,7 @@ files:
91
34
  - lib/code_teams.rb
92
35
  - lib/code_teams/plugin.rb
93
36
  - lib/code_teams/plugins/identity.rb
37
+ - lib/code_teams/utils.rb
94
38
  homepage: https://github.com/rubyatscale/code_teams
95
39
  licenses:
96
40
  - MIT
@@ -99,7 +43,6 @@ metadata:
99
43
  source_code_uri: https://github.com/rubyatscale/code_teams
100
44
  changelog_uri: https://github.com/rubyatscale/code_teams/releases
101
45
  allowed_push_host: https://rubygems.org
102
- post_install_message:
103
46
  rdoc_options: []
104
47
  require_paths:
105
48
  - lib
@@ -107,15 +50,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
107
50
  requirements:
108
51
  - - ">="
109
52
  - !ruby/object:Gem::Version
110
- version: '2.6'
53
+ version: '3.2'
111
54
  required_rubygems_version: !ruby/object:Gem::Requirement
112
55
  requirements:
113
56
  - - ">="
114
57
  - !ruby/object:Gem::Version
115
58
  version: '0'
116
59
  requirements: []
117
- rubygems_version: 3.1.6
118
- signing_key:
60
+ rubygems_version: 3.6.7
119
61
  specification_version: 4
120
62
  summary: A low-dependency gem for declaring and querying engineering teams
121
63
  test_files: []