owners 0.0.0 → 0.0.1

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
  SHA1:
3
- metadata.gz: 03249f63f81c9db5877a0f55d0562c78e725afc6
4
- data.tar.gz: 4cc529092d6d620a6cc1c652c819f9d7b63d662f
3
+ metadata.gz: 65952d9a4159c6a54c705d6a4d52a251a34eacb7
4
+ data.tar.gz: d414a754b03b3549b017bbe3cff45ce511f69487
5
5
  SHA512:
6
- metadata.gz: ff1c532d637a42f59b985764d90f686629ac3be3fefc8bca062eda6be54f141ca599f86c88aedc6b6e22d5ff940b9a6e20c7ac236bbfc3015150a3a2d50123e7
7
- data.tar.gz: c6fe901e8fe7467cb8eda35f12d6d2d468e4c0b8a2999c935d8e092c57de18339926834202490fa6f2e191bdf1791a40b7a7e243caf6a953c57b812653b7e525
6
+ metadata.gz: 255d1ef9d4f7cf7c984d194e8224bc44c02ec72a26adaa2731955990d556bb111d306924675cd5b0a95cae364fb9738105474cc476774c93e675288a194bb719
7
+ data.tar.gz: 64a897f374262fb422aef1ea8181ae28ac4c8181318fc05db1a41cee6015626b7c09a5b767bd7df89d1fe705c757eaf4989bceaca9f51d77cadcca7f2c92bb4e
@@ -0,0 +1,11 @@
1
+ addons:
2
+ code_climate:
3
+ repo_token: ca03d82be038f1e592be540bc81030c2cc69101d13f99acfd1889bbd47d17824
4
+
5
+ install: bundle install --jobs 3 --retry 3
6
+
7
+ rvm:
8
+ - 2.0.0
9
+ - ruby-head
10
+
11
+ script: bundle exec rspec
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
- source 'https://www.rubygems.org'
1
+ source "https://www.rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'codeclimate-test-reporter'
5
+ gem "codeclimate-test-reporter"
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/shuber/owners.svg)](http://travis-ci.org/shuber/owners) [![Code Climate](https://codeclimate.com/github/shuber/owners/badges/gpa.svg)](https://codeclimate.com/github/shuber/owners) [![Coverage](https://codeclimate.com/github/shuber/owners/badges/coverage.svg)](https://codeclimate.com/github/shuber/owners) [![Gem Version](https://badge.fury.io/rb/owners.svg)](http://badge.fury.io/rb/owners)
4
4
 
5
- Take ownership of your code
5
+ Take ownership of your code.
6
6
 
7
7
 
8
8
  ## Installation
@@ -16,7 +16,7 @@ gem install owners
16
16
 
17
17
  Define an `OWNERS` file in any directory within your repository. This file should contain a newline separated list of subscribers to notify when files within the directory have changed. The `OWNERS` files are searched recursively up the tree to make organizing owners more convenient.
18
18
 
19
- Subscribers can be anything that suits your needs e.g. emails, GitHub handles, or Slack channels.
19
+ Subscribers can be anything that suits your needs e.g. emails, GitHub handles, Slack channels, etc.
20
20
 
21
21
  ```
22
22
  bob@your-org.com
@@ -34,18 +34,20 @@ The `OWNERS` file also supports limiting paths with regular expressions or exact
34
34
  bob@demo.com lib/bobs_special_file.rb
35
35
  ```
36
36
 
37
- Once your `OWNERS` files are defined, you can search for a list of owners by calling `Owners.for` with a list of paths e.g. output from `git diff --name-only`
37
+ Once your `OWNERS` files are defined, you can search for a list of owners by calling `Owners.for` with a list of paths e.g. output from `git diff --name-only`.
38
38
 
39
39
  ```ruby
40
40
  Owners.for(".env", "app/controllers/posts_controller.rb", "app/models/user.rb")
41
41
  ```
42
42
 
43
- This method returns a unique array of all the owners who have subscribed to changes for the specified files.
43
+ This method returns a unique array of all the owners who have subscribed to changes for the specified files. These subscribers can then be notified however you see fit!
44
44
 
45
45
  ## API
46
46
 
47
47
  [YARD Documentation](http://www.rubydoc.info/github/shuber/owners)
48
48
 
49
+ * `Owners.file`
50
+ * `Owners.file=`
49
51
  * `Owners.for`
50
52
 
51
53
 
@@ -1,10 +1,29 @@
1
- require 'owners/config'
2
- require 'owners/path'
3
- require 'owners/search'
4
- require 'owners/version'
1
+ require "pathname"
2
+ require "set"
3
+ require "owners/config"
4
+ require "owners/search"
5
+ require "owners/tree"
6
+ require "owners/version"
5
7
 
6
8
  module Owners
7
- def self.for(*paths)
8
- Search.new(paths).owners
9
+ class << self
10
+ # @api public
11
+ attr_writer :file
12
+
13
+ # The name of the file used to store ownership
14
+ # subscriptions. Defaults to OWNERS.
15
+ #
16
+ # @api public
17
+ def file
18
+ @file ||= "OWNERS"
19
+ end
20
+
21
+ # Accepts a list of file paths and returns an array of
22
+ # owners that have subscribed to the specified files.
23
+ #
24
+ # @api public
25
+ def for(*files)
26
+ Search.new(files).owners
27
+ end
9
28
  end
10
29
  end
@@ -1,22 +1,32 @@
1
1
  module Owners
2
+ # Parses an OWNERS file and returns an array of owners
3
+ # that have subscribed to a specified file path.
4
+ #
5
+ # @api private
2
6
  class Config
3
- attr_reader :root
4
-
5
7
  def initialize(file)
6
- @config = File.read(file)
7
- @root = File.dirname(file)
8
+ @config = file.read
9
+ @root = file.dirname.to_s
8
10
  end
9
11
 
10
- def owners
11
- subscriptions.each_with_object({}) do |line, hash|
12
- owner, path = line.split(/\s+/, 2).push('.*')
13
- hash[owner] ||= []
14
- hash[owner] << Regexp.new(path)
12
+ def owners(path)
13
+ if path.start_with?(@root)
14
+ relative = path.sub("#{@root}/", "")
15
+
16
+ search do |subscription, results|
17
+ owner, pattern = subscription.split(/\s+/, 2)
18
+ regex = Regexp.new(pattern || ".*")
19
+ results << owner if regex =~ relative
20
+ end
15
21
  end
16
22
  end
17
23
 
18
24
  private
19
25
 
26
+ def search(&block)
27
+ subscriptions.each_with_object([], &block)
28
+ end
29
+
20
30
  def subscriptions
21
31
  @config.split("\n").reject(&:empty?)
22
32
  end
@@ -1,17 +1,44 @@
1
1
  module Owners
2
+ # Accepts an array of file paths and returns an array of
3
+ # owners that have subscribed to the specified files.
4
+ #
5
+ # @api private
2
6
  class Search
3
- def initialize(paths)
4
- @paths = paths
7
+ def initialize(files)
8
+ @files = files
5
9
  end
6
10
 
7
11
  def owners
8
- paths.flat_map(&:owners).flatten.uniq.sort
12
+ search do |(path, config), results|
13
+ owners = config.owners(path.to_s)
14
+ results.merge(owners) if owners
15
+ end
9
16
  end
10
17
 
11
18
  private
12
19
 
20
+ def search(&block)
21
+ attempts.each_with_object(SortedSet.new, &block).to_a
22
+ end
23
+
24
+ def attempts
25
+ paths.product(configs)
26
+ end
27
+
28
+ def configs
29
+ subscriptions.map { |file| Config.new(file) }
30
+ end
31
+
32
+ def subscriptions
33
+ trees.flat_map(&:owner_files).uniq
34
+ end
35
+
36
+ def trees
37
+ paths.map { |path| Tree.new(path) }
38
+ end
39
+
13
40
  def paths
14
- @paths.map { |path| Path.new(path) }
41
+ @files.map { |file| Pathname.new(file) }
15
42
  end
16
43
  end
17
44
  end
@@ -0,0 +1,32 @@
1
+ module Owners
2
+ # Traverses up the directory tree starting at a specified
3
+ # file and returns an array of all OWNERS files.
4
+ #
5
+ # @api private
6
+ class Tree
7
+ def initialize(file)
8
+ @file = file
9
+ end
10
+
11
+ def owner_files
12
+ parents.each_with_object([]) do |parent, files|
13
+ config = parent.join(Owners.file)
14
+ files << config if config.file?
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def parents
21
+ parents = []
22
+ file = @file
23
+
24
+ until file == file.dirname
25
+ file = file.dirname
26
+ parents << file
27
+ end
28
+
29
+ parents
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,3 @@
1
1
  module Owners
2
- VERSION = '0.0.0'
2
+ VERSION = "0.0.1"
3
3
  end
@@ -1,19 +1,19 @@
1
- require File.expand_path('../lib/owners/version', __FILE__)
1
+ require File.expand_path("../lib/owners/version", __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
- s.author = 'Sean Huber'
5
- s.email = 'github@shuber.io'
4
+ s.author = "Sean Huber"
5
+ s.email = "github@shuber.io"
6
6
  s.extra_rdoc_files = %w(LICENSE)
7
7
  s.files = `git ls-files`.split("\n")
8
- s.homepage = 'https://github.com/shuber/owners'
9
- s.license = 'MIT'
10
- s.name = 'owners'
8
+ s.homepage = "https://github.com/shuber/owners"
9
+ s.license = "MIT"
10
+ s.name = "owners"
11
11
  s.rdoc_options = %w(--charset=UTF-8 --inline-source --line-numbers --main README.md)
12
12
  s.require_paths = %w(lib)
13
- s.required_ruby_version = '>= 2.0.0'
14
- s.summary = 'Take ownership of your code'
13
+ s.required_ruby_version = ">= 2.0.0"
14
+ s.summary = "Take ownership of your code"
15
15
  s.test_files = `git ls-files -- spec/*`.split("\n")
16
16
  s.version = Owners::VERSION
17
17
 
18
- s.add_development_dependency 'rspec'
18
+ s.add_development_dependency "rspec"
19
19
  end
@@ -1,47 +1,47 @@
1
1
  RSpec.describe Owners do
2
- describe '.for' do
2
+ describe ".for" do
3
3
  subject { described_class.for(*paths) }
4
4
 
5
- context 'with one path' do
6
- let(:paths) { ['example/app/controllers/users_controller.rb'] }
5
+ context "with one path" do
6
+ let(:paths) { ["example/app/controllers/users_controller.rb"] }
7
7
 
8
- it 'parses owners correctly' do
9
- expect(subject).to eq(['@org/auth', '@org/blog'])
8
+ it "parses owners correctly" do
9
+ expect(subject).to eq(["@org/auth", "@org/blog"])
10
10
  end
11
11
  end
12
12
 
13
- context 'with multiple paths' do
13
+ context "with multiple paths" do
14
14
  let(:paths) {[
15
- 'example/app/controllers/posts_controller.rb',
16
- 'example/app/models/user.rb',
15
+ "example/app/controllers/posts_controller.rb",
16
+ "example/app/models/user.rb",
17
17
  ]}
18
18
 
19
- it 'parses owners correctly' do
20
- expect(subject).to eq(['@org/auth', '@org/blog', 'data@example.com'])
19
+ it "parses owners correctly" do
20
+ expect(subject).to eq(["@org/auth", "@org/blog", "data@example.com"])
21
21
  end
22
22
  end
23
23
 
24
- context 'with no matches' do
25
- let(:paths) { ['some-path-without-owners'] }
24
+ context "with no matches" do
25
+ let(:paths) { ["some-path-without-owners"] }
26
26
 
27
- it 'parses owners correctly' do
27
+ it "parses owners correctly" do
28
28
  expect(subject).to be_empty
29
29
  end
30
30
  end
31
31
 
32
- context 'with a regex matcher' do
33
- let(:paths) { ['example/app/models/blog.rb'] }
32
+ context "with a regex matcher" do
33
+ let(:paths) { ["example/app/models/blog.rb"] }
34
34
 
35
- it 'parses owners correctly' do
36
- expect(subject).to eq(['@blogger', '@org/blog', 'data@example.com'])
35
+ it "parses owners correctly" do
36
+ expect(subject).to eq(["@blogger", "@org/blog", "data@example.com"])
37
37
  end
38
38
  end
39
39
 
40
- context 'with a rule containing whitespace' do
41
- let(:paths) { ['example/app/models/post.rb'] }
40
+ context "with a rule containing whitespace" do
41
+ let(:paths) { ["example/app/models/post.rb"] }
42
42
 
43
- it 'parses owners correctly' do
44
- expect(subject).to eq(['@org/blog', '@whitespace', 'data@example.com'])
43
+ it "parses owners correctly" do
44
+ expect(subject).to eq(["@org/blog", "@whitespace", "data@example.com"])
45
45
  end
46
46
  end
47
47
  end
@@ -1,12 +1,12 @@
1
- if ENV['CODECLIMATE_REPO_TOKEN']
2
- require 'codeclimate-test-reporter'
1
+ if ENV["CODECLIMATE_REPO_TOKEN"]
2
+ require "codeclimate-test-reporter"
3
3
  CodeClimate::TestReporter.start
4
4
  else
5
- require 'simplecov'
6
- SimpleCov.start { add_filter('/vendor/bundle/') }
5
+ require "simplecov"
6
+ SimpleCov.start { add_filter("/vendor/bundle/") }
7
7
  end
8
8
 
9
- require File.expand_path('../../lib/owners', __FILE__)
9
+ require File.expand_path("../../lib/owners", __FILE__)
10
10
 
11
11
  RSpec.configure do |config|
12
12
  config.filter_run :focus
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: owners
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Huber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-21 00:00:00.000000000 Z
11
+ date: 2015-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -33,6 +33,7 @@ extra_rdoc_files:
33
33
  files:
34
34
  - ".gitignore"
35
35
  - ".rspec"
36
+ - ".travis.yml"
36
37
  - Gemfile
37
38
  - Gemfile.lock
38
39
  - LICENSE
@@ -47,9 +48,8 @@ files:
47
48
  - example/app/models/user.rb
48
49
  - lib/owners.rb
49
50
  - lib/owners/config.rb
50
- - lib/owners/core_ext
51
- - lib/owners/path.rb
52
51
  - lib/owners/search.rb
52
+ - lib/owners/tree.rb
53
53
  - lib/owners/version.rb
54
54
  - owners.gemspec
55
55
  - spec/owners_spec.rb
File without changes
@@ -1,41 +0,0 @@
1
- module Owners
2
- class Path
3
- CONFIG = 'OWNERS'
4
-
5
- def initialize(file)
6
- @file = file
7
- end
8
-
9
- def owners
10
- configs.each_with_object([]) do |config, owners|
11
- path = @file.sub("#{config.root}/", '')
12
-
13
- config.owners.each do |owner, regexes|
14
- regexes.each do |regex|
15
- if path =~ regex
16
- owners << owner
17
- end
18
- end
19
- end
20
- end
21
- end
22
-
23
- private
24
-
25
- def configs
26
- configs = []
27
- file = @file
28
-
29
- until ['.', '/'].include?(file)
30
- file = File.dirname(file)
31
- config = File.join(file, CONFIG)
32
-
33
- if File.exists?(config) && !File.directory?(config)
34
- configs << Config.new(config)
35
- end
36
- end
37
-
38
- configs
39
- end
40
- end
41
- end