From e9a91fb1190b8e5a34bb38f7f13a2f15657cdf88 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Tue, 27 May 2025 14:49:34 -0600 Subject: [PATCH 01/41] Add rspec binstub --- bin/rspec | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 bin/rspec diff --git a/bin/rspec b/bin/rspec new file mode 100755 index 0000000..cb53ebe --- /dev/null +++ b/bin/rspec @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rspec' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +bundle_binstub = File.expand_path("bundle", __dir__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rspec-core", "rspec") From da7338f81b52f226ce5efdfd9849c4a7ca9bc0e5 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Tue, 27 May 2025 15:24:54 -0600 Subject: [PATCH 02/41] Add happy path of Plugin auto-helper-method --- lib/code_teams.rb | 16 ++++++++++++ lib/code_teams/plugin.rb | 4 +++ lib/code_teams/utils.rb | 11 +++++++++ spec/integration/plugin_helper_spec.rb | 34 ++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 lib/code_teams/utils.rb create mode 100644 spec/integration/plugin_helper_spec.rb diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 95d825c..d03e4d8 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -7,6 +7,7 @@ require 'sorbet-runtime' require 'code_teams/plugin' require 'code_teams/plugins/identity' +require 'code_teams/utils' module CodeTeams extend T::Sig @@ -14,6 +15,7 @@ module CodeTeams class IncorrectPublicApiUsageError < StandardError; end UNKNOWN_TEAM_STRING = 'Unknown Team' + @plugins_registered = false sig { returns(T::Array[Team]) } def self.all @@ -35,6 +37,11 @@ def self.find(name) sig { params(dir: String).returns(T::Array[Team]) } def self.for_directory(dir) + unless @plugins_registered + Team.register_plugins + @plugins_registered = true + end + Pathname.new(dir).glob('**/*.yml').map do |path| Team.from_yml(path.to_s) rescue Psych::SyntaxError @@ -85,6 +92,15 @@ def self.from_hash(raw_hash) ) end + sig { void } + def self.register_plugins + Plugin.all_plugins.each do |plugin| + define_method(plugin.root_key) do + plugin.for(self).public_send(plugin.root_key) + end + end + end + sig { returns(T::Hash[T.untyped, T.untyped]) } attr_reader :raw_hash diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index b87ea21..16674ec 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -10,6 +10,10 @@ class Plugin abstract! + def self.root_key + Utils.underscore(name.split('::').last) + end + sig { params(team: Team).void } def initialize(team) @team = team diff --git a/lib/code_teams/utils.rb b/lib/code_teams/utils.rb new file mode 100644 index 0000000..590b4de --- /dev/null +++ b/lib/code_teams/utils.rb @@ -0,0 +1,11 @@ +module CodeTeams + class Utils + def self.underscore(string) + string.gsub('::', '/') + .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') + .gsub(/([a-z\d])([A-Z])/, '\1_\2') + .tr('-', '_') + .downcase + end + end +end diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb new file mode 100644 index 0000000..f51be4d --- /dev/null +++ b/spec/integration/plugin_helper_spec.rb @@ -0,0 +1,34 @@ +RSpec.describe CodeTeams::Plugin, 'helper integration' do + def write_team_yml(extra_data: false) + write_file('config/teams/my_team.yml', <<~YML.strip) + name: My Team + extra_data: #{extra_data} + YML + end + + before do + CodeTeams.bust_caches! + + test_plugin_class = Class.new(described_class) do + MyStruct = Data.define(:foo, :bar) + + def test_plugin + data = @team.raw_hash['extra_data'] + MyStruct.new(data['foo'], data['bar']) + end + end + + stub_const('TestPlugin', test_plugin_class) + end + + describe 'helper methods' do + it 'adds a helper method to the team' do + write_team_yml(extra_data: { foo: 'foo', bar: 'bar' }) + + team = CodeTeams.find('My Team') + + expect(team.test_plugin.foo).to eq('foo') + expect(team.test_plugin.bar).to eq('bar') + end + end +end From 6485514789dda75cde40e7375bbebd0a630795af Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 09:37:50 -0600 Subject: [PATCH 03/41] Test nested data --- spec/integration/plugin_helper_spec.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index f51be4d..cdd666a 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -22,13 +22,17 @@ def test_plugin end describe 'helper methods' do + let(:team) { CodeTeams.find('My Team') } + it 'adds a helper method to the team' do write_team_yml(extra_data: { foo: 'foo', bar: 'bar' }) - - team = CodeTeams.find('My Team') - expect(team.test_plugin.foo).to eq('foo') expect(team.test_plugin.bar).to eq('bar') end + + it 'supports nested data' do + write_team_yml(extra_data: { foo: { bar: 'bar' } }) + expect(team.test_plugin.foo['bar']).to eq('bar') + end end end From 4878f9663e7cf61eddf812ce96127e067ba6f223 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 09:59:12 -0600 Subject: [PATCH 04/41] Reset registered plugins during bust_caches --- lib/code_teams.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index d03e4d8..759f039 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -66,6 +66,7 @@ def self.tag_value_for(string) # 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 sig { void } def self.bust_caches! + @plugins_registered = false Plugin.bust_caches! @all = nil @index_by_name = nil From ef9b6be5225691ff0f49bea50a7b99df7753339a Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 09:59:28 -0600 Subject: [PATCH 05/41] Support all public instance methods on a given plugin --- lib/code_teams.rb | 7 ++- spec/integration/plugin_helper_spec.rb | 60 ++++++++++++++++++-------- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 759f039..21db418 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -96,8 +96,11 @@ def self.from_hash(raw_hash) sig { void } def self.register_plugins Plugin.all_plugins.each do |plugin| - define_method(plugin.root_key) do - plugin.for(self).public_send(plugin.root_key) + # All public instance methods only defined in the plugin class + plugin.instance_methods(false).each do |method| + define_method(method) do + plugin.for(self).public_send(method) + end end end end diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index cdd666a..711e036 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -8,31 +8,57 @@ def write_team_yml(extra_data: false) before do CodeTeams.bust_caches! + write_team_yml(extra_data: { foo: 'foo', bar: 'bar' }) + end - test_plugin_class = Class.new(described_class) do - MyStruct = Data.define(:foo, :bar) + describe 'helper methods' do + context 'with a single implicit method' do + before do + test_plugin_class = Class.new(described_class) do + def test_plugin + data = @team.raw_hash['extra_data'] + Data.define(:foo, :bar).new(data['foo'], data['bar']) + end + end - def test_plugin - data = @team.raw_hash['extra_data'] - MyStruct.new(data['foo'], data['bar']) + stub_const('TestPlugin', test_plugin_class) end - end - stub_const('TestPlugin', test_plugin_class) - end + it 'adds a helper method to the team' do + team = CodeTeams.find('My Team') - describe 'helper methods' do - let(:team) { CodeTeams.find('My Team') } + expect(team.test_plugin.foo).to eq('foo') + expect(team.test_plugin.bar).to eq('bar') + end - it 'adds a helper method to the team' do - write_team_yml(extra_data: { foo: 'foo', bar: 'bar' }) - expect(team.test_plugin.foo).to eq('foo') - expect(team.test_plugin.bar).to eq('bar') + it 'supports nested data' do + write_team_yml(extra_data: { foo: { bar: 'bar' } }) + team = CodeTeams.find('My Team') + expect(team.test_plugin.foo['bar']).to eq('bar') + end end - it 'supports nested data' do - write_team_yml(extra_data: { foo: { bar: 'bar' } }) - expect(team.test_plugin.foo['bar']).to eq('bar') + context 'with other public methods' do + before do + test_plugin_class = Class.new(described_class) do + def other_method1 + 'other1' + end + + def other_method2 + 'other2' + end + end + + stub_const('TestPlugin', test_plugin_class) + end + + it 'adds the other methods to the team' do + team = CodeTeams.find('My Team') + + expect(team.other_method1).to eq('other1') + expect(team.other_method2).to eq('other2') + end end end end From c17a9a507721b2bc844ba559e6a06a84fdc274e3 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 10:07:02 -0600 Subject: [PATCH 06/41] Move test setup helpers into shared support with proper mixin config --- spec/integration/plugin_helper_spec.rb | 7 ------- spec/lib/code_teams/plugin_spec.rb | 7 ------- spec/spec_helper.rb | 8 ++------ spec/support/io_helpers.rb | 18 ++++++++++++++++++ 4 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 spec/support/io_helpers.rb diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index 711e036..fafcf7e 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -1,11 +1,4 @@ RSpec.describe CodeTeams::Plugin, 'helper integration' do - def write_team_yml(extra_data: false) - write_file('config/teams/my_team.yml', <<~YML.strip) - name: My Team - extra_data: #{extra_data} - YML - end - before do CodeTeams.bust_caches! write_team_yml(extra_data: { foo: 'foo', bar: 'bar' }) diff --git a/spec/lib/code_teams/plugin_spec.rb b/spec/lib/code_teams/plugin_spec.rb index d9bdc2d..4ac5e14 100644 --- a/spec/lib/code_teams/plugin_spec.rb +++ b/spec/lib/code_teams/plugin_spec.rb @@ -1,11 +1,4 @@ RSpec.describe CodeTeams::Plugin do - def write_team_yml(extra_data: false) - write_file('config/teams/my_team.yml', <<~YML.strip) - name: My Team - extra_data: #{extra_data} - YML - end - before do CodeTeams.bust_caches! diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2f23b3e..02ad9a1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,8 @@ require 'pry' require 'code_teams' +Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f } + RSpec.configure do |config| # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = '.rspec_status' @@ -23,9 +25,3 @@ FileUtils.rm_rf(tmpdir) end end - -def write_file(path, content = '') - pathname = Pathname.new(path) - FileUtils.mkdir_p(pathname.dirname) - pathname.write(content) -end diff --git a/spec/support/io_helpers.rb b/spec/support/io_helpers.rb new file mode 100644 index 0000000..9779c4c --- /dev/null +++ b/spec/support/io_helpers.rb @@ -0,0 +1,18 @@ +module IOHelpers + def write_team_yml(extra_data: false) + write_file('config/teams/my_team.yml', <<~YML.strip) + name: My Team + extra_data: #{extra_data} + YML + end + + def write_file(path, content = '') + pathname = Pathname.new(path) + FileUtils.mkdir_p(pathname.dirname) + pathname.write(content) + end +end + +RSpec.configure do |config| + config.include IOHelpers +end From e598d4540682e010b5f84f47a603ab21e8399eac Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 10:08:05 -0600 Subject: [PATCH 07/41] Remove Utils, not needed now --- lib/code_teams.rb | 1 - lib/code_teams/plugin.rb | 4 ---- lib/code_teams/utils.rb | 11 ----------- 3 files changed, 16 deletions(-) delete mode 100644 lib/code_teams/utils.rb diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 21db418..8f18e24 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -7,7 +7,6 @@ require 'sorbet-runtime' require 'code_teams/plugin' require 'code_teams/plugins/identity' -require 'code_teams/utils' module CodeTeams extend T::Sig diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index 16674ec..b87ea21 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -10,10 +10,6 @@ class Plugin abstract! - def self.root_key - Utils.underscore(name.split('::').last) - end - sig { params(team: Team).void } def initialize(team) @team = team diff --git a/lib/code_teams/utils.rb b/lib/code_teams/utils.rb deleted file mode 100644 index 590b4de..0000000 --- a/lib/code_teams/utils.rb +++ /dev/null @@ -1,11 +0,0 @@ -module CodeTeams - class Utils - def self.underscore(string) - string.gsub('::', '/') - .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') - .gsub(/([a-z\d])([A-Z])/, '\1_\2') - .tr('-', '_') - .downcase - end - end -end From 849662cb70d9f7d5b6e877b2306b2eda7936f372 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 10:28:47 -0600 Subject: [PATCH 08/41] Revert "Remove Utils, not needed now" This reverts commit 3daff59ac9c1dedde182db1b52b8bc4a2867cf53. --- lib/code_teams.rb | 1 + lib/code_teams/plugin.rb | 4 ++++ lib/code_teams/utils.rb | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 lib/code_teams/utils.rb diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 8f18e24..21db418 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -7,6 +7,7 @@ require 'sorbet-runtime' require 'code_teams/plugin' require 'code_teams/plugins/identity' +require 'code_teams/utils' module CodeTeams extend T::Sig diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index b87ea21..16674ec 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -10,6 +10,10 @@ class Plugin abstract! + def self.root_key + Utils.underscore(name.split('::').last) + end + sig { params(team: Team).void } def initialize(team) @team = team diff --git a/lib/code_teams/utils.rb b/lib/code_teams/utils.rb new file mode 100644 index 0000000..590b4de --- /dev/null +++ b/lib/code_teams/utils.rb @@ -0,0 +1,11 @@ +module CodeTeams + class Utils + def self.underscore(string) + string.gsub('::', '/') + .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') + .gsub(/([a-z\d])([A-Z])/, '\1_\2') + .tr('-', '_') + .downcase + end + end +end From 6c17d087a84437d13ddee20443fceb9951022ec5 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 10:45:22 -0600 Subject: [PATCH 09/41] Cannot support secondary methods yet --- lib/code_teams.rb | 7 ++----- spec/integration/plugin_helper_spec.rb | 10 ++++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 21db418..759f039 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -96,11 +96,8 @@ def self.from_hash(raw_hash) sig { void } def self.register_plugins Plugin.all_plugins.each do |plugin| - # All public instance methods only defined in the plugin class - plugin.instance_methods(false).each do |method| - define_method(method) do - plugin.for(self).public_send(method) - end + define_method(plugin.root_key) do + plugin.for(self).public_send(plugin.root_key) end end end diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index fafcf7e..5f347c2 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -34,6 +34,10 @@ def test_plugin context 'with other public methods' do before do test_plugin_class = Class.new(described_class) do + def test_plugin + Data.define(:foo).new('foo') + end + def other_method1 'other1' end @@ -47,10 +51,12 @@ def other_method2 end it 'adds the other methods to the team' do + skip 'TODO: cannot support in this version' team = CodeTeams.find('My Team') - expect(team.other_method1).to eq('other1') - expect(team.other_method2).to eq('other2') + expect(team.test_plugin.foo).to eq('foo') + expect(team.test_plugin.other_method1).to eq('other1') + expect(team.test_plugin.other_method2).to eq('other2') end end end From 8e2f67c881457c50b92e3bdb6df0c77973f92a93 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 10:50:37 -0600 Subject: [PATCH 10/41] Add sorbet def --- lib/code_teams/plugin.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index 16674ec..8c928e0 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -10,6 +10,7 @@ class Plugin abstract! + sig { returns(String) } def self.root_key Utils.underscore(name.split('::').last) end From 69b298b6c4b886056576c2e3e2cc9fb517213577 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 11:06:01 -0600 Subject: [PATCH 11/41] Add Utils.demodulize --- lib/code_teams/plugin.rb | 2 +- lib/code_teams/utils.rb | 4 ++++ spec/lib/code_teams/plugin_spec.rb | 9 +++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index 8c928e0..3adf64b 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -12,7 +12,7 @@ class Plugin sig { returns(String) } def self.root_key - Utils.underscore(name.split('::').last) + Utils.underscore(Utils.demodulize(name)) end sig { params(team: Team).void } diff --git a/lib/code_teams/utils.rb b/lib/code_teams/utils.rb index 590b4de..527a51a 100644 --- a/lib/code_teams/utils.rb +++ b/lib/code_teams/utils.rb @@ -7,5 +7,9 @@ def self.underscore(string) .tr('-', '_') .downcase end + + def self.demodulize(string) + string.split('::').last + end end end diff --git a/spec/lib/code_teams/plugin_spec.rb b/spec/lib/code_teams/plugin_spec.rb index 4ac5e14..b992aee 100644 --- a/spec/lib/code_teams/plugin_spec.rb +++ b/spec/lib/code_teams/plugin_spec.rb @@ -21,4 +21,13 @@ def extra_data expect(TestPlugin.for(team).extra_data).to be(false) end end + + describe '.root_key' do + it 'returns the underscore version of the plugin name' do + test_plugin_class = Class.new(described_class) + stub_const('FooNamespace::TestPlugin', test_plugin_class) + + expect(FooNamespace::TestPlugin.root_key).to eq('test_plugin') + end + end end From 586b8f6827c5d5ed7e4071687d19c1b18edaba0c Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 11:09:07 -0600 Subject: [PATCH 12/41] Add TODO for overriding Plugin.root_key --- spec/lib/code_teams/plugin_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/lib/code_teams/plugin_spec.rb b/spec/lib/code_teams/plugin_spec.rb index b992aee..53ef4b8 100644 --- a/spec/lib/code_teams/plugin_spec.rb +++ b/spec/lib/code_teams/plugin_spec.rb @@ -29,5 +29,9 @@ def extra_data expect(FooNamespace::TestPlugin.root_key).to eq('test_plugin') end + + it 'can be overridden by a subclass' do + skip 'TODO: implement' + end end end From 46d3154d5b34794a2fc4b4185e8ab777351fc35b Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 11:17:41 -0600 Subject: [PATCH 13/41] Add root_key overriding with declaration method --- lib/code_teams/plugin.rb | 15 ++++++++++----- spec/lib/code_teams/plugin_spec.rb | 7 ++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index 3adf64b..93fd5b7 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -10,16 +10,21 @@ class Plugin abstract! - sig { returns(String) } - def self.root_key - Utils.underscore(Utils.demodulize(name)) - end - sig { params(team: Team).void } def initialize(team) @team = team end + sig { params(key: String).returns(String) } + def self.root_key(key = default_root_key) + @root_key ||= key + end + + sig { returns(String) } + def self.default_root_key + Utils.underscore(Utils.demodulize(name)) + end + sig { params(base: T.untyped).void } def self.inherited(base) # rubocop:disable Lint/MissingSuper all_plugins << T.cast(base, T.class_of(Plugin)) diff --git a/spec/lib/code_teams/plugin_spec.rb b/spec/lib/code_teams/plugin_spec.rb index 53ef4b8..8c72acf 100644 --- a/spec/lib/code_teams/plugin_spec.rb +++ b/spec/lib/code_teams/plugin_spec.rb @@ -31,7 +31,12 @@ def extra_data end it 'can be overridden by a subclass' do - skip 'TODO: implement' + test_plugin_class = Class.new(described_class) do + root_key 'foo' + end + stub_const('TestPlugin', test_plugin_class) + + expect(TestPlugin.root_key).to eq('foo') end end end From 2a23305c7a86954f6a3cca66d70ef03fd510c272 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 14:07:45 -0600 Subject: [PATCH 14/41] Bump version 1.0.2 -> 1.1.0 --- Gemfile.lock | 2 +- code_teams.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index cc7ddc0..353543b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - code_teams (1.0.2) + code_teams (1.1.0) sorbet-runtime GEM diff --git a/code_teams.gemspec b/code_teams.gemspec index 533451e..9a9a1f7 100644 --- a/code_teams.gemspec +++ b/code_teams.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = 'code_teams' - spec.version = '1.0.2' + spec.version = '1.1.0' spec.authors = ['Gusto Engineers'] spec.email = ['dev@gusto.com'] spec.summary = 'A low-dependency gem for declaring and querying engineering teams' From 323720e01684aa4d4f9a3efd351d856668de246b Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:19:56 -0600 Subject: [PATCH 15/41] Rename root_key to data_accessor_name --- lib/code_teams.rb | 4 ++-- lib/code_teams/plugin.rb | 6 +++--- spec/lib/code_teams/plugin_spec.rb | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 759f039..41fad61 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -96,8 +96,8 @@ def self.from_hash(raw_hash) sig { void } def self.register_plugins Plugin.all_plugins.each do |plugin| - define_method(plugin.root_key) do - plugin.for(self).public_send(plugin.root_key) + define_method(plugin.data_accessor_name) do + plugin.for(self).public_send(plugin.data_accessor_name) end end end diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index 93fd5b7..e08b29f 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -16,12 +16,12 @@ def initialize(team) end sig { params(key: String).returns(String) } - def self.root_key(key = default_root_key) - @root_key ||= key + def self.data_accessor_name(key = default_data_accessor_name) + @data_accessor_name ||= key end sig { returns(String) } - def self.default_root_key + def self.default_data_accessor_name Utils.underscore(Utils.demodulize(name)) end diff --git a/spec/lib/code_teams/plugin_spec.rb b/spec/lib/code_teams/plugin_spec.rb index 8c72acf..e13a2e9 100644 --- a/spec/lib/code_teams/plugin_spec.rb +++ b/spec/lib/code_teams/plugin_spec.rb @@ -22,21 +22,21 @@ def extra_data end end - describe '.root_key' do + describe '.data_accessor_name' do it 'returns the underscore version of the plugin name' do test_plugin_class = Class.new(described_class) stub_const('FooNamespace::TestPlugin', test_plugin_class) - expect(FooNamespace::TestPlugin.root_key).to eq('test_plugin') + expect(FooNamespace::TestPlugin.data_accessor_name).to eq('test_plugin') end it 'can be overridden by a subclass' do test_plugin_class = Class.new(described_class) do - root_key 'foo' + data_accessor_name 'foo' end stub_const('TestPlugin', test_plugin_class) - expect(TestPlugin.root_key).to eq('foo') + expect(TestPlugin.data_accessor_name).to eq('foo') end end end From 7516410341cfd2e1b060cd6cd8abc759628b9fec Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:23:13 -0600 Subject: [PATCH 16/41] Add integration for data_accessor_name override --- spec/integration/plugin_helper_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index 5f347c2..87f233f 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -29,6 +29,26 @@ def test_plugin team = CodeTeams.find('My Team') expect(team.test_plugin.foo['bar']).to eq('bar') end + + context 'when the data accessor name is overridden' do + before do + test_plugin_class = Class.new(described_class) do + data_accessor_name 'foo' + + def foo + Data.define(:bar).new('bar') + end + end + + stub_const('TestPlugin', test_plugin_class) + end + + it 'adds the data accessor name to the team' do + team = CodeTeams.find('My Team') + + expect(team.foo.bar).to eq('bar') + end + end end context 'with other public methods' do From 6e062fe07a6e701da04f6e03d70f5f981df975c5 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:24:34 -0600 Subject: [PATCH 17/41] DRY up the integration test --- spec/integration/plugin_helper_spec.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index 87f233f..ae480d1 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -4,6 +4,8 @@ write_team_yml(extra_data: { foo: 'foo', bar: 'bar' }) end + let(:team) { CodeTeams.find('My Team') } + describe 'helper methods' do context 'with a single implicit method' do before do @@ -18,15 +20,12 @@ def test_plugin end it 'adds a helper method to the team' do - team = CodeTeams.find('My Team') - expect(team.test_plugin.foo).to eq('foo') expect(team.test_plugin.bar).to eq('bar') end it 'supports nested data' do write_team_yml(extra_data: { foo: { bar: 'bar' } }) - team = CodeTeams.find('My Team') expect(team.test_plugin.foo['bar']).to eq('bar') end @@ -44,8 +43,6 @@ def foo end it 'adds the data accessor name to the team' do - team = CodeTeams.find('My Team') - expect(team.foo.bar).to eq('bar') end end @@ -72,8 +69,6 @@ def other_method2 it 'adds the other methods to the team' do skip 'TODO: cannot support in this version' - team = CodeTeams.find('My Team') - expect(team.test_plugin.foo).to eq('foo') expect(team.test_plugin.other_method1).to eq('other1') expect(team.test_plugin.other_method2).to eq('other2') From 16adda55f312b5f582022b27f6f094de2628a237 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:30:12 -0600 Subject: [PATCH 18/41] Update README --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 05ee4a9..fda4432 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,26 @@ github: 1) You can now use the following API to get GitHub information about that team: ```ruby team = CodeTeams.find('My Team') -MyGithubPlugin.for(team).github +members = team.github.members +team_name = team.github.team ``` + +1a) Or if your accessor method name differs from your plugin's class name: + +```ruby +class MyPlugin < CodeTeams::Plugin + data_accessor_name :other_name + + def other_name + # ... + end +end + +# You can then use: +team.other_name +# similarly to the Github example above +``` + 2) Running team validations (see below) will ensure all teams have a GitHub team specified Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for: From da563a98241f720503a46e29a24344d4affa1e76 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:31:05 -0600 Subject: [PATCH 19/41] Formatting --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fda4432..431a397 100644 --- a/README.md +++ b/README.md @@ -65,21 +65,21 @@ members = team.github.members team_name = team.github.team ``` -1a) Or if your accessor method name differs from your plugin's class name: + a) Or if your accessor method name differs from your plugin's class name: -```ruby -class MyPlugin < CodeTeams::Plugin - data_accessor_name :other_name + ```ruby + class MyPlugin < CodeTeams::Plugin + data_accessor_name :other_name - def other_name - # ... - end -end + def other_name + # ... + end + end -# You can then use: -team.other_name -# similarly to the Github example above -``` + # You can then use: + team.other_name + # similarly to the Github example above + ``` 2) Running team validations (see below) will ensure all teams have a GitHub team specified From 605df12e18e274189639fd45b5a7db3f4cfeae9e Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:31:30 -0600 Subject: [PATCH 20/41] README formatting --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 431a397..dec83c2 100644 --- a/README.md +++ b/README.md @@ -65,21 +65,21 @@ members = team.github.members team_name = team.github.team ``` - a) Or if your accessor method name differs from your plugin's class name: + a) Or if your accessor method name differs from your plugin's class name: - ```ruby - class MyPlugin < CodeTeams::Plugin - data_accessor_name :other_name + ```ruby + class MyPlugin < CodeTeams::Plugin + data_accessor_name :other_name - def other_name - # ... - end + def other_name + # ... end + end - # You can then use: - team.other_name - # similarly to the Github example above - ``` + # You can then use: + team.other_name + # similarly to the Github example above + ``` 2) Running team validations (see below) will ensure all teams have a GitHub team specified From 83fbcc67f7bb2910abff6fb477668d636bacd1ea Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:33:27 -0600 Subject: [PATCH 21/41] README formatting --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index dec83c2..b63194f 100644 --- a/README.md +++ b/README.md @@ -65,23 +65,23 @@ members = team.github.members team_name = team.github.team ``` - a) Or if your accessor method name differs from your plugin's class name: + * Or if your accessor method name differs from your plugin's class name: - ```ruby - class MyPlugin < CodeTeams::Plugin - data_accessor_name :other_name + ```ruby + class MyPlugin < CodeTeams::Plugin + data_accessor_name :other_name - def other_name - # ... - end - end + def other_name + # ... + end + end - # You can then use: - team.other_name - # similarly to the Github example above - ``` + # You can then use: + team.other_name + # similarly to the Github example above + ``` -2) Running team validations (see below) will ensure all teams have a GitHub team specified +1) Running team validations (see below) will ensure all teams have a GitHub team specified Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for: - Identifying which teams own which feature flags From 38dc8c8720993512d80b976bf7a4ad6c1f57f4a5 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:36:21 -0600 Subject: [PATCH 22/41] README formatting --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b63194f..f17aaa8 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,10 @@ team_name = team.github.team # similarly to the Github example above ``` -1) Running team validations (see below) will ensure all teams have a GitHub team specified + However, to avoid confusion, it's recommended to use the naming convention + whenever possible so that your accessor name matches your plugin's name + +2) Running team validations (see below) will ensure all teams have a GitHub team specified Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for: - Identifying which teams own which feature flags From c7f1052462a8041348376dc030c98e9f787e8e11 Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Wed, 28 May 2025 15:42:24 -0600 Subject: [PATCH 23/41] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f17aaa8..7b12045 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ github: ```ruby team = CodeTeams.find('My Team') members = team.github.members -team_name = team.github.team +github_name = team.github.team ``` * Or if your accessor method name differs from your plugin's class name: From daeb9b4a97be49751a39136120cdafbe3a8ad51c Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Wed, 28 May 2025 15:46:04 -0600 Subject: [PATCH 24/41] Add backward-compatibility integration spec --- spec/integration/plugin_helper_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index ae480d1..a349d06 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -75,4 +75,16 @@ def other_method2 end end end + + specify 'backwards compatibility' do + test_plugin_class = Class.new(described_class) do + def test_plugin + Data.define(:foo).new('foo') + end + end + + stub_const('TestPlugin', test_plugin_class) + + expect(TestPlugin.for(team).test_plugin.foo).to eq('foo') + end end From ede535d3f2565a6fa3f1c162ca161dd732cdef50 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Thu, 29 May 2025 08:27:12 -0600 Subject: [PATCH 25/41] PR feedback --- README.md | 29 ++++++++++++++++------------- lib/code_teams/utils.rb | 24 ++++++++++++------------ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 7b12045..b03ac55 100644 --- a/README.md +++ b/README.md @@ -59,26 +59,29 @@ github: ``` 1) You can now use the following API to get GitHub information about that team: -```ruby -team = CodeTeams.find('My Team') -members = team.github.members -github_name = team.github.team -``` - * Or if your accessor method name differs from your plugin's class name: + ```ruby + team = CodeTeams.find('My Team') + members = team.github.members + github_name = team.github.team + ``` + + Alternatively, you can assign an accessor method name that differs from the plugin's class name: - ```ruby - class MyPlugin < CodeTeams::Plugin - data_accessor_name :other_name + ```ruby + class MyPlugin < CodeTeams::Plugin + data_accessor_name :other_name - def other_name - # ... - end - end + def other_name + # ... + end + end # You can then use: team.other_name # similarly to the Github example above + # You can then access data in the following manner: + team.other_name.attribute_name ``` However, to avoid confusion, it's recommended to use the naming convention diff --git a/lib/code_teams/utils.rb b/lib/code_teams/utils.rb index 527a51a..493e3bd 100644 --- a/lib/code_teams/utils.rb +++ b/lib/code_teams/utils.rb @@ -1,15 +1,15 @@ -module CodeTeams - class Utils - def self.underscore(string) - string.gsub('::', '/') - .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') - .gsub(/([a-z\d])([A-Z])/, '\1_\2') - .tr('-', '_') - .downcase - end +module CodeTeams::Utils + extend self - def self.demodulize(string) - string.split('::').last - end + def underscore(string) + string.gsub('::', '/') + .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') + .gsub(/([a-z\d])([A-Z])/, '\1_\2') + .tr('-', '_') + .downcase + end + + def demodulize(string) + string.split('::').last end end From 07444d37d608b8887a312dbc6e354ca5030c6461 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Thu, 29 May 2025 08:29:04 -0600 Subject: [PATCH 26/41] Markdown indentation --- README.md | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index b03ac55..4b7d4a6 100644 --- a/README.md +++ b/README.md @@ -60,41 +60,42 @@ github: 1) You can now use the following API to get GitHub information about that team: - ```ruby - team = CodeTeams.find('My Team') - members = team.github.members - github_name = team.github.team - ``` + ```ruby + team = CodeTeams.find('My Team') + members = team.github.members + github_name = team.github.team + ``` - Alternatively, you can assign an accessor method name that differs from the plugin's class name: + Alternatively, you can assign an accessor method name that differs from the plugin's class name: - ```ruby - class MyPlugin < CodeTeams::Plugin - data_accessor_name :other_name + ```ruby + class MyPlugin < CodeTeams::Plugin + data_accessor_name :other_name - def other_name - # ... + def other_name + # ... + end end - end - # You can then use: - team.other_name - # similarly to the Github example above - # You can then access data in the following manner: - team.other_name.attribute_name - ``` + # You can then use: + team.other_name + # similarly to the Github example above + # You can then access data in the following manner: + team.other_name.attribute_name + ``` - However, to avoid confusion, it's recommended to use the naming convention - whenever possible so that your accessor name matches your plugin's name + However, to avoid confusion, it's recommended to use the naming convention + whenever possible so that your accessor name matches your plugin's name 2) Running team validations (see below) will ensure all teams have a GitHub team specified -Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for: -- Identifying which teams own which feature flags -- Mapping teams to specific portions of the code through `code_ownership` -- Allowing teams to protect certain files and require approval on modification of certain files -- Specifying owned dependencies (Ruby gems, JavaScript packages, and more) -- Specifying how to get in touch with the team via Slack (their channel and handle) + Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for: + + - Identifying which teams own which feature flags + - Mapping teams to specific portions of the code through `code_ownership` + - Allowing teams to protect certain files and require approval on modification of certain files + - Specifying owned dependencies (Ruby gems, JavaScript packages, and more) + - Specifying how to get in touch with the team via Slack (their channel and handle) ## Configuration You'll want to ensure that all teams are valid in your CI environment. We recommend running code like this in CI: From c4cced2bd4cd037446328bc75f0828371ea69a8a Mon Sep 17 00:00:00 2001 From: Valerie Burzynski Date: Thu, 29 May 2025 14:17:14 -0500 Subject: [PATCH 27/41] refactor: use longform definition of modules over shorthand --- lib/code_teams/utils.rb | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/code_teams/utils.rb b/lib/code_teams/utils.rb index 493e3bd..cf65d87 100644 --- a/lib/code_teams/utils.rb +++ b/lib/code_teams/utils.rb @@ -1,15 +1,17 @@ -module CodeTeams::Utils - extend self +module CodeTeams + module Utils + extend self - def underscore(string) - string.gsub('::', '/') - .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') - .gsub(/([a-z\d])([A-Z])/, '\1_\2') - .tr('-', '_') - .downcase - end + def underscore(string) + string.gsub('::', '/') + .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') + .gsub(/([a-z\d])([A-Z])/, '\1_\2') + .tr('-', '_') + .downcase + end - def demodulize(string) - string.split('::').last + def demodulize(string) + string.split('::').last + end end end From 7e18f42b99d5d99ddcef2a5584b9408aad98666d Mon Sep 17 00:00:00 2001 From: Valerie Burzynski Date: Thu, 29 May 2025 14:17:55 -0500 Subject: [PATCH 28/41] style: fix rubocop Style/ModuleFunction issue --- lib/code_teams/utils.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/code_teams/utils.rb b/lib/code_teams/utils.rb index cf65d87..281cddb 100644 --- a/lib/code_teams/utils.rb +++ b/lib/code_teams/utils.rb @@ -1,6 +1,6 @@ module CodeTeams module Utils - extend self + module_function def underscore(string) string.gsub('::', '/') From 9b23a7be52f88ee0cf004ff34571f632d14f9e3b Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Thu, 29 May 2025 13:54:59 -0600 Subject: [PATCH 29/41] Update spec/integration/plugin_helper_spec.rb Co-authored-by: Valerie Burzynski --- spec/integration/plugin_helper_spec.rb | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index a349d06..1ff5e92 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -48,32 +48,6 @@ def foo end end - context 'with other public methods' do - before do - test_plugin_class = Class.new(described_class) do - def test_plugin - Data.define(:foo).new('foo') - end - - def other_method1 - 'other1' - end - - def other_method2 - 'other2' - end - end - - stub_const('TestPlugin', test_plugin_class) - end - - it 'adds the other methods to the team' do - skip 'TODO: cannot support in this version' - expect(team.test_plugin.foo).to eq('foo') - expect(team.test_plugin.other_method1).to eq('other1') - expect(team.test_plugin.other_method2).to eq('other2') - end - end end specify 'backwards compatibility' do From 834ea3b13bee8ff6366b7c5383ee961a817a0381 Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Fri, 30 May 2025 14:30:59 -0600 Subject: [PATCH 30/41] Update code_teams.rb --- lib/code_teams.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 41fad61..404304d 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -97,6 +97,7 @@ def self.from_hash(raw_hash) def self.register_plugins Plugin.all_plugins.each do |plugin| define_method(plugin.data_accessor_name) do + # e.g., MyGithubPlugin.for(team).github plugin.for(self).public_send(plugin.data_accessor_name) end end From c640b9236c4198f02b45883163bc568586d605c6 Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Fri, 30 May 2025 14:31:44 -0600 Subject: [PATCH 31/41] Update code_teams.rb --- lib/code_teams.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 404304d..d721f33 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -96,6 +96,7 @@ def self.from_hash(raw_hash) sig { void } def self.register_plugins Plugin.all_plugins.each do |plugin| + # e.g. def github (on Team) define_method(plugin.data_accessor_name) do # e.g., MyGithubPlugin.for(team).github plugin.for(self).public_send(plugin.data_accessor_name) From 99ec7542395027d1615221b384a5f3a379abe004 Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Fri, 30 May 2025 14:32:05 -0600 Subject: [PATCH 32/41] Update code_teams.rb --- lib/code_teams.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index d721f33..67470cb 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -96,7 +96,7 @@ def self.from_hash(raw_hash) sig { void } def self.register_plugins Plugin.all_plugins.each do |plugin| - # e.g. def github (on Team) + # e.g., def github (on Team) define_method(plugin.data_accessor_name) do # e.g., MyGithubPlugin.for(team).github plugin.for(self).public_send(plugin.data_accessor_name) From d9e2bfd2778fb5f987c7dcf1f47439057fbbd567 Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Fri, 30 May 2025 14:33:21 -0600 Subject: [PATCH 33/41] Update plugin.rb --- lib/code_teams/plugin.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index e08b29f..611c456 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -22,6 +22,7 @@ def self.data_accessor_name(key = default_data_accessor_name) sig { returns(String) } def self.default_data_accessor_name + # e.g., MyNamespace::MyPlugin -> my_plugin Utils.underscore(Utils.demodulize(name)) end From 8ffb2a0527c56e25df140e75dc01c309a0a03b0b Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Fri, 30 May 2025 14:42:18 -0600 Subject: [PATCH 34/41] Update .rubocop.yml --- .rubocop.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index f9e12bb..7feb128 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -110,6 +110,9 @@ Layout/MultilineMethodCallIndentation: Style/BlockDelimiters: Enabled: false +Style/StringLiterals: + Enabled: false + # Sometimes we like methods like `get_packages` Naming/AccessorMethodName: Enabled: false From 070c7a0e63ae9e93f0e99d67c04f0d3f74977fec Mon Sep 17 00:00:00 2001 From: Joe Sak Date: Fri, 30 May 2025 14:51:30 -0600 Subject: [PATCH 35/41] Update plugin_helper_spec.rb --- spec/integration/plugin_helper_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index 1ff5e92..9c00b4c 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -47,7 +47,6 @@ def foo end end end - end specify 'backwards compatibility' do From 16fe1a72fd27e7ddd00e50b66accfd61c52b5428 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Fri, 30 May 2025 15:26:40 -0600 Subject: [PATCH 36/41] Fix rubocop target version, io helpers, helper usage --- spec/integration/plugin_helper_spec.rb | 4 ++-- spec/support/io_helpers.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/integration/plugin_helper_spec.rb index 9c00b4c..d14d520 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/integration/plugin_helper_spec.rb @@ -1,7 +1,7 @@ RSpec.describe CodeTeams::Plugin, 'helper integration' do before do CodeTeams.bust_caches! - write_team_yml(extra_data: { foo: 'foo', bar: 'bar' }) + write_team_yml(extra_data: { 'foo' => 'foo', 'bar' => 'bar' }) end let(:team) { CodeTeams.find('My Team') } @@ -25,7 +25,7 @@ def test_plugin end it 'supports nested data' do - write_team_yml(extra_data: { foo: { bar: 'bar' } }) + write_team_yml(extra_data: { 'foo' => { 'bar' => 'bar' } }) expect(team.test_plugin.foo['bar']).to eq('bar') end diff --git a/spec/support/io_helpers.rb b/spec/support/io_helpers.rb index 9779c4c..f9cb30c 100644 --- a/spec/support/io_helpers.rb +++ b/spec/support/io_helpers.rb @@ -1,9 +1,9 @@ module IOHelpers def write_team_yml(extra_data: false) - write_file('config/teams/my_team.yml', <<~YML.strip) - name: My Team - extra_data: #{extra_data} - YML + write_file('config/teams/my_team.yml', YAML.dump({ + name: 'My Team', + extra_data: extra_data + }.transform_keys(&:to_s))) end def write_file(path, content = '') From f18d7b633fa026d263326239e4d05dab4f1988b8 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Fri, 30 May 2025 15:30:43 -0600 Subject: [PATCH 37/41] Ruby 3.2 maintenance --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 02ad9a1..087ba37 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ require 'pry' require 'code_teams' -Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f } +Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f } RSpec.configure do |config| # Enable flags like --only-failures and --next-failure From 5ce0dca9988c5c24705fbf72face48229b2824af Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Fri, 30 May 2025 15:33:46 -0600 Subject: [PATCH 38/41] Sorbet issues fixed --- lib/code_teams.rb | 4 ++-- lib/code_teams/plugin.rb | 2 ++ sorbet/rbi/todo.rbi | 1 + spec/lib/code_teams/plugin_spec.rb | 33 +++++++++++++++--------------- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/code_teams.rb b/lib/code_teams.rb index 67470cb..c1f341c 100644 --- a/lib/code_teams.rb +++ b/lib/code_teams.rb @@ -15,7 +15,7 @@ module CodeTeams class IncorrectPublicApiUsageError < StandardError; end UNKNOWN_TEAM_STRING = 'Unknown Team' - @plugins_registered = false + @plugins_registered = T.let(false, T::Boolean) sig { returns(T::Array[Team]) } def self.all @@ -99,7 +99,7 @@ def self.register_plugins # e.g., def github (on Team) define_method(plugin.data_accessor_name) do # e.g., MyGithubPlugin.for(team).github - plugin.for(self).public_send(plugin.data_accessor_name) + plugin.for(T.cast(self, Team)).public_send(plugin.data_accessor_name) end end end diff --git a/lib/code_teams/plugin.rb b/lib/code_teams/plugin.rb index 611c456..2735f31 100644 --- a/lib/code_teams/plugin.rb +++ b/lib/code_teams/plugin.rb @@ -10,6 +10,8 @@ class Plugin abstract! + @data_accessor_name = T.let(nil, T.nilable(String)) + sig { params(team: Team).void } def initialize(team) @team = team diff --git a/sorbet/rbi/todo.rbi b/sorbet/rbi/todo.rbi index 5845608..389b1a4 100644 --- a/sorbet/rbi/todo.rbi +++ b/sorbet/rbi/todo.rbi @@ -4,3 +4,4 @@ # typed: strong module ::RSpec; end module ::TestPlugin; end +module TestNamespace::TestPlugin; end diff --git a/spec/lib/code_teams/plugin_spec.rb b/spec/lib/code_teams/plugin_spec.rb index e13a2e9..e2ba61f 100644 --- a/spec/lib/code_teams/plugin_spec.rb +++ b/spec/lib/code_teams/plugin_spec.rb @@ -1,42 +1,41 @@ -RSpec.describe CodeTeams::Plugin do - before do - CodeTeams.bust_caches! - - test_plugin_class = Class.new(described_class) do - def extra_data - @team.raw_hash['extra_data'] - end - end - stub_const('TestPlugin', test_plugin_class) - end +module TestNamespace; end +RSpec.describe CodeTeams::Plugin do describe '.bust_caches!' do it 'clears all plugins team registries ensuring cached configs are purged' do + test_plugin_class = Class.new(described_class) do + def extra_data + @team.raw_hash['extra_data'] + end + end + stub_const('TestNamespace::TestPlugin', test_plugin_class) + + CodeTeams.bust_caches! write_team_yml(extra_data: true) team = CodeTeams.find('My Team') - expect(TestPlugin.for(team).extra_data).to be(true) + expect(TestNamespace::TestPlugin.for(team).extra_data).to be(true) write_team_yml(extra_data: false) CodeTeams.bust_caches! team = CodeTeams.find('My Team') - expect(TestPlugin.for(team).extra_data).to be(false) + expect(TestNamespace::TestPlugin.for(team).extra_data).to be(false) end end describe '.data_accessor_name' do it 'returns the underscore version of the plugin name' do test_plugin_class = Class.new(described_class) - stub_const('FooNamespace::TestPlugin', test_plugin_class) + stub_const('TestNamespace::TestPlugin', test_plugin_class) - expect(FooNamespace::TestPlugin.data_accessor_name).to eq('test_plugin') + expect(TestNamespace::TestPlugin.data_accessor_name).to eq('test_plugin') end it 'can be overridden by a subclass' do test_plugin_class = Class.new(described_class) do data_accessor_name 'foo' end - stub_const('TestPlugin', test_plugin_class) + stub_const('TestNamespace::TestPlugin', test_plugin_class) - expect(TestPlugin.data_accessor_name).to eq('foo') + expect(TestNamespace::TestPlugin.data_accessor_name).to eq('foo') end end end From 7384e038c3831f454ea9d863422b8314cf285dc0 Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Mon, 2 Jun 2025 15:04:07 -0600 Subject: [PATCH 39/41] Update the bundle --- Gemfile.lock | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 353543b..8cda53d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,7 +8,7 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - benchmark (0.4.0) + benchmark (0.4.1) coderay (1.1.3) diff-lcs (1.6.2) erubi (1.13.1) @@ -71,13 +71,14 @@ GEM lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) - sorbet (0.5.12134) - sorbet-static (= 0.5.12134) - sorbet-runtime (0.5.12134) - sorbet-static (0.5.12134-universal-darwin) - sorbet-static-and-runtime (0.5.12134) - sorbet (= 0.5.12134) - sorbet-runtime (= 0.5.12134) + sorbet (0.5.12142) + sorbet-static (= 0.5.12142) + sorbet-runtime (0.5.12142) + sorbet-static (0.5.12142-universal-darwin) + sorbet-static (0.5.12142-x86_64-linux) + sorbet-static-and-runtime (0.5.12142) + sorbet (= 0.5.12142) + sorbet-runtime (= 0.5.12142) spoom (1.6.3) erubi (>= 1.10.0) prism (>= 0.28.0) From a29287e13ab97e96f62d964d4d9223d975e61f7f Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Mon, 2 Jun 2025 15:14:34 -0600 Subject: [PATCH 40/41] Add tapioca and ignore vendor/bundle --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b04a8c8..85f65a4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /doc/ /pkg/ /spec/reports/ +/vendor/bundle /tmp/ # rspec failure tracking From 0f4d661226610c5759ec8860f56b054e17b3daba Mon Sep 17 00:00:00 2001 From: Joseph Sak Date: Mon, 2 Jun 2025 16:06:04 -0600 Subject: [PATCH 41/41] Rubocop --- .../plugin_helper_integration_spec.rb} | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) rename spec/{integration/plugin_helper_spec.rb => code_teams/plugin_helper_integration_spec.rb} (71%) diff --git a/spec/integration/plugin_helper_spec.rb b/spec/code_teams/plugin_helper_integration_spec.rb similarity index 71% rename from spec/integration/plugin_helper_spec.rb rename to spec/code_teams/plugin_helper_integration_spec.rb index d14d520..ca5b40d 100644 --- a/spec/integration/plugin_helper_spec.rb +++ b/spec/code_teams/plugin_helper_integration_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe CodeTeams::Plugin, 'helper integration' do +RSpec.describe CodeTeams::Plugin do before do CodeTeams.bust_caches! write_team_yml(extra_data: { 'foo' => 'foo', 'bar' => 'bar' }) @@ -28,23 +28,23 @@ def test_plugin write_team_yml(extra_data: { 'foo' => { 'bar' => 'bar' } }) expect(team.test_plugin.foo['bar']).to eq('bar') end + end - context 'when the data accessor name is overridden' do - before do - test_plugin_class = Class.new(described_class) do - data_accessor_name 'foo' + context 'when the data accessor name is overridden' do + before do + test_plugin_class = Class.new(described_class) do + data_accessor_name 'foo' - def foo - Data.define(:bar).new('bar') - end + def foo + Data.define(:bar).new('bar') end - - stub_const('TestPlugin', test_plugin_class) end - it 'adds the data accessor name to the team' do - expect(team.foo.bar).to eq('bar') - end + stub_const('TestPlugin', test_plugin_class) + end + + it 'adds the data accessor name to the team' do + expect(team.foo.bar).to eq('bar') end end end