├── .gitignore ├── .projections.json ├── .rspec ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── prj ├── completion.bash ├── lib ├── projectionist.rb └── projectionist │ ├── cli.rb │ ├── errors.rb │ ├── projections.rb │ └── version.rb ├── projectionist.gemspec └── spec ├── cli_spec.rb ├── helper.rb └── projections_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | *.bundle 19 | *.so 20 | *.o 21 | *.a 22 | mkmf.log 23 | spec/fixtures/**/* 24 | -------------------------------------------------------------------------------- /.projections.json: -------------------------------------------------------------------------------- 1 | { 2 | "README.md": { "type": "readme" }, 3 | "lib/*.rb": { 4 | "type": "lib", 5 | "alternate": "spec/{}_spec.rb" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format Fivemat 2 | --color 3 | --order random 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1.2 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Griffin Smith 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/projectionist.svg)](http://badge.fury.io/rb/projectionist) 2 | [![Build Status](https://travis-ci.org/glittershark/projectionist.svg?branch=master)](https://travis-ci.org/glittershark/projectionist) 3 | [![Coverage Status](https://coveralls.io/repos/glittershark/projectionist/badge.png?branch=master)](https://coveralls.io/r/glittershark/projectionist?branch=master) 4 | [![Code Climate](https://codeclimate.com/github/glittershark/projectionist.png)](https://codeclimate.com/github/glittershark/projectionist) 5 | [![Dependency Status](https://gemnasium.com/glittershark/projectionist.svg)](https://gemnasium.com/glittershark/projectionist) 6 | 7 | # Projectionist 8 | 9 | Quickly edit project files using the 10 | [.projections.json](https://github.com/tpope/vim-projectionist) format 11 | 12 | ## Installation 13 | 14 | $ gem install projectionist 15 | 16 | There's also a bash completion script, that you can install globally by running: 17 | 18 | ```bash 19 | $ curl -OL 20 | https://github.com/glittershark/projectionist/raw/master/completion.bash 21 | ``` 22 | 23 | or locally by downloading [this 24 | file](https://github.com/glittershark/projectionist/raw/master/completion.bash) 25 | and sourcing it in your `~/.bashrc` 26 | 27 | Zsh completion is [on the way!](https://github.com/glittershark/projectionist/issues/18) 28 | 29 | ## Usage 30 | 31 | For ease of typing, the executable file for projectionist is `prj`. 32 | 33 | Given a `.projections.json` file in the root of your project with the following 34 | structure: 35 | 36 | ``` { "lib/**/*.rb": { "type": "lib" } } ``` 37 | 38 | The command to edit `lib/whatever/test.rb` would be: 39 | 40 | $ prj edit lib whatever/test 41 | 42 | Note that there are two glob components here - `**` and `*`. When editing files, 43 | these components are separated by a `/` 44 | 45 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task default: :spec 7 | -------------------------------------------------------------------------------- /bin/prj: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'projectionist/cli' 4 | 5 | Projectionist::CLI.start(ARGV) 6 | -------------------------------------------------------------------------------- /completion.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | _prj() { 4 | local cur prev opts 5 | COMPREPLY=() 6 | cur="${COMP_WORDS[COMP_CWORD]}" 7 | prev="${COMP_WORDS[COMP_CWORD-1]}" 8 | cmds="edit help list types" 9 | 10 | case $prev in 11 | prj | help) 12 | COMPREPLY=( $(compgen -W "${cmds}" -- ${cur}) ) 13 | return 0 14 | ;; 15 | edit | types) 16 | types=$(prj types) 17 | COMPREPLY=( $(compgen -W "${types}" -- ${cur}) ) 18 | ;; 19 | *) 20 | # Test if we're editing a type 21 | if [ "${COMP_WORDS[COMP_CWORD-2]}" = 'edit' ]; then 22 | files=$(prj list ${prev}) 23 | COMPREPLY=( $(compgen -W "${files}" -- ${cur}) ) 24 | fi 25 | ;; 26 | esac 27 | } 28 | complete -F _prj prj 29 | 30 | -------------------------------------------------------------------------------- /lib/projectionist.rb: -------------------------------------------------------------------------------- 1 | require 'projectionist/version' 2 | require 'projectionist/projections' 3 | require 'projectionist/cli' 4 | require 'projectionist/errors' 5 | 6 | module Projectionist 7 | end 8 | -------------------------------------------------------------------------------- /lib/projectionist/cli.rb: -------------------------------------------------------------------------------- 1 | require 'projectionist' 2 | require 'thor' 3 | 4 | module Projectionist 5 | class CLI < Thor 6 | attr_accessor :projections 7 | 8 | def initialize(*) 9 | @projections = Projectionist::Projections.new 10 | super 11 | rescue Projectionist::ProjectionError => error 12 | $stderr.puts "ERROR: #{error.message}" 13 | exit 1 14 | end 15 | 16 | desc 'edit []', 'Edit the file for named ' 17 | option :editor 18 | def edit(type, file='') 19 | file = @projections.file_for type, file 20 | exec "#{editor} #{file}" 21 | end 22 | 23 | desc 'list [-v|--verbose] ', 'List all files for ' 24 | option :verbose, type: :boolean, aliases: '-v', 25 | desc: "List full file paths instead of file names" 26 | def list(type) 27 | puts @projections.files_for(type, verbose: options[:verbose]).join("\n") 28 | end 29 | 30 | desc 'types', 'List all types' 31 | def types 32 | puts @projections.types.join("\n") 33 | end 34 | 35 | no_commands do 36 | def editor 37 | editor = options[:editor] || ENV['VISUAL'] || ENV['EDITOR'] || 'vim' 38 | editor.empty? ? 'vim' : editor 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/projectionist/errors.rb: -------------------------------------------------------------------------------- 1 | module Projectionist 2 | class ProjectionError < Exception 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /lib/projectionist/projections.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'projectionist/errors' 3 | 4 | module Projectionist 5 | class Projections 6 | attr_reader :json_file_existed 7 | 8 | def initialize 9 | @type_hash = {} 10 | load_file 11 | end 12 | 13 | def load_file(path = nil) 14 | @json = get_json path 15 | @json.each do |glob, options| 16 | next unless options.key? 'type' 17 | if glob.include? '**/*' 18 | raise Projectionist::ProjectionError, 'Globs may not include `**/*`' 19 | end 20 | @type_hash[options['type']] = options.merge('glob' => glob) 21 | end 22 | end 23 | 24 | def type?(type) 25 | @type_hash.key?(type) 26 | end 27 | 28 | def types 29 | @type_hash.keys 30 | end 31 | 32 | def file_for(type, name='') 33 | return nil unless type? type 34 | 35 | Dir.chdir @basedir 36 | glob = build_glob(@type_hash[type]['glob'], name) 37 | file = Dir.glob(glob)[0] 38 | File.expand_path(file.nil? ? glob : file) 39 | end 40 | 41 | def files_for(type, options = {}) 42 | return [] unless type? type 43 | 44 | glob = glob_for type 45 | files = Dir.glob(glob) 46 | files.map do |p| 47 | if options[:verbose] 48 | File.expand_path(p) 49 | else 50 | p.match(glob_to_regex glob) do |m| 51 | components = m.captures.reject(&:empty?).map { |s| s.sub('/', '') } 52 | components.join('/') 53 | end 54 | end 55 | end 56 | end 57 | 58 | private 59 | 60 | def get_json(path) 61 | if path.nil? 62 | path = projections_path 63 | if path.nil? 64 | @json_file_existed = false 65 | return {} 66 | end 67 | end 68 | @json_file_existed = true 69 | File.open(path, 'r') { |f| JSON.parse(f.read) } 70 | end 71 | 72 | def projections_path 73 | path = File.expand_path './.projections.json' 74 | until File.exist? path 75 | return nil if [false, '/'].include?(File.dirname path) 76 | path = File.expand_path('../../.projections.json', path) 77 | end 78 | @basedir = File.dirname path 79 | path 80 | end 81 | 82 | def build_glob(glob, file) 83 | # Split the passed file by `/`, then replace all globs that use `*` or 84 | # `**` with components of the passed file, in order 85 | if glob.include? '**' 86 | file_components = file.split('/') 87 | glob_components = glob.split(/\*+/) 88 | glob_components.zip(file_components).flatten.compact.join('') 89 | else 90 | glob.sub(/\*/, file) 91 | end 92 | end 93 | 94 | def glob_for(type) 95 | glob = @type_hash[type]['glob'] 96 | glob = glob.sub('/*', '/**/*') unless glob.include? '**' 97 | glob 98 | end 99 | 100 | 101 | def glob_to_regex(glob) 102 | glob.match(/(.*)[*]{2}\/(.*)/) do |m| 103 | m[1] + '([^\x00]*?)' + m[2].gsub('*', '([^/\x00]*?)') 104 | end 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/projectionist/version.rb: -------------------------------------------------------------------------------- 1 | module Projectionist 2 | VERSION = '0.3.0' 3 | end 4 | -------------------------------------------------------------------------------- /projectionist.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'projectionist/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'projectionist' 8 | spec.version = Projectionist::VERSION 9 | spec.authors = ['Griffin Smith'] 10 | spec.email = ['wildgriffin45@gmail.com'] 11 | spec.summary = 'Command line interface to the .projections.json format' 12 | spec.description = <<-EOF 13 | Projectionist allows you to quickly edit files in a project from the command 14 | line, using the projections.json format 15 | EOF 16 | spec.homepage = 'http://github.com/glittershark/projectionist' 17 | spec.license = 'MIT' 18 | 19 | spec.files = `git ls-files -z`.split("\x0") 20 | spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) } 21 | spec.test_files = spec.files.grep(/^(test|spec|features)\//) 22 | spec.require_paths = ['lib'] 23 | 24 | spec.add_development_dependency 'bundler', '~> 1.6' 25 | spec.add_development_dependency 'rake', '~> 10.3' 26 | spec.add_development_dependency 'rspec', '~> 3.0' 27 | spec.add_development_dependency 'fivemat', '~> 1.3' 28 | spec.add_development_dependency 'coveralls', '~> 0.7' 29 | 30 | spec.add_runtime_dependency 'thor', '~> 0.19.1', '>= 0.19' 31 | end 32 | -------------------------------------------------------------------------------- /spec/cli_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Projectionist::CLI do 4 | let(:test_dir) { File.join(fixture_folder, 'test') } 5 | let(:test_file) { File.join(test_dir, 'file.rb') } 6 | let!(:old_cwd) { Dir.pwd } 7 | 8 | before do 9 | FileUtils.rm_rf fixture_folder 10 | Dir.mkdir fixture_folder 11 | write_fixtures('test/*.rb' => { 'type' => 'test' }) 12 | Dir.mkdir test_dir 13 | File.open(test_file, 'w') 14 | Dir.chdir fixture_folder 15 | end 16 | 17 | after { Dir.chdir old_cwd } 18 | 19 | context 'with bad projections' do 20 | before do 21 | write_fixtures('test/**/*.rb' => { 'type' => 'bad' }) 22 | end 23 | 24 | it 'prints out an error and exits' do 25 | cli = Projectionist::CLI.allocate 26 | expect(cli).to receive(:exit).with(1) 27 | expect { cli.send(:initialize) }.to output("ERROR: Globs may not include `**/*`\n").to_stderr 28 | end 29 | end 30 | 31 | describe '#edit' do 32 | context 'with options[:editor] and $EDITOR set' do 33 | before do 34 | subject.options = { editor: '/use/this/one' } 35 | ENV['EDITOR'] = '/this/is/test/two' 36 | end 37 | 38 | it 'edits the file with options[:editor]' do 39 | expect(subject).to receive(:exec).with("/use/this/one #{test_file}") 40 | subject.edit 'test', 'file' 41 | end 42 | end 43 | 44 | context 'with $VISUAL and $EDITOR set, but not options[:editor]' do 45 | before do 46 | subject.options = {} 47 | ENV['VISUAL'] = '/this/is/test/one' 48 | ENV['EDITOR'] = '/this/is/test/two' 49 | end 50 | 51 | it 'edits the file with $VISUAL' do 52 | expect(subject).to receive(:exec).with("/this/is/test/one #{test_file}") 53 | subject.edit 'test', 'file' 54 | end 55 | end 56 | 57 | context 'with $EDITOR set but not $VISUAL or options[:editor]' do 58 | before do 59 | subject.options = {} 60 | ENV.delete('VISUAL') 61 | ENV['EDITOR'] = '/this/is/a/test' 62 | end 63 | 64 | it 'edits the file with $EDITOR' do 65 | expect(subject).to receive(:exec).with("/this/is/a/test #{test_file}") 66 | subject.edit 'test', 'file' 67 | end 68 | end 69 | 70 | context 'with none of options[:editor], $VISUAL, or $EDITOR set' do 71 | before do 72 | subject.options = {} 73 | ENV.delete('VISUAL') 74 | ENV.delete('EDITOR') 75 | end 76 | 77 | it 'edits the file with `vim`' do 78 | expect(subject).to receive(:exec).with("vim #{test_file}") 79 | subject.edit 'test', 'file' 80 | end 81 | end 82 | 83 | context 'with empty strings for the environment variables' do 84 | before do 85 | subject.options = {} 86 | ENV['VISUAL'] = '' 87 | ENV['EDITOR'] = '' 88 | end 89 | 90 | it 'edits the file with `vim`' do 91 | expect(subject).to receive(:exec).with("vim #{test_file}") 92 | subject.edit 'test', 'file' 93 | end 94 | end 95 | end 96 | 97 | describe '#list' do 98 | context 'with options[:verbose]=false' do 99 | before do 100 | subject.options = { verbose: false } 101 | end 102 | 103 | it 'prints out the files for the given type, one per line' do 104 | expect { subject.list 'test' }.to output("file\n").to_stdout 105 | end 106 | end 107 | 108 | context 'with options[:verbose]=true' do 109 | before do 110 | subject.options = { verbose: true } 111 | end 112 | 113 | it 'prints out the full paths for the given type, one per line' do 114 | expect { subject.list 'test' }.to output("#{test_file}\n").to_stdout 115 | end 116 | end 117 | end 118 | 119 | describe '#types' do 120 | it 'prints out the list of possible projection types, one per line' do 121 | expect { subject.types }.to output("test\n").to_stdout 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /spec/helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | 3 | require 'coveralls' 4 | Coveralls.wear! 5 | 6 | require 'projectionist' 7 | require 'json' 8 | require 'fileutils' 9 | require 'tmpdir' 10 | 11 | RSpec.configure do |config| 12 | config.before(:suite) { $tmpdir = Dir.mktmpdir } 13 | config.after(:suite) { FileUtils.remove_entry_secure $tmpdir } 14 | 15 | config.expect_with :rspec do |c| 16 | c.syntax = :expect 17 | end 18 | end 19 | 20 | def fixture_folder 21 | $tmpdir 22 | end 23 | 24 | def fixture_file 25 | File.join(fixture_folder, '.projections.json') 26 | end 27 | 28 | def read_fixtures 29 | File.open(fixture_file, 'r') { |f| JSON.parse(f.read) } 30 | end 31 | 32 | def write_fixtures(hash) 33 | File.open(fixture_file, 'w') { |f| f.write(hash.to_json) } 34 | end 35 | 36 | def delete_fixtures 37 | File.delete fixture_file if File.exist? fixture_file 38 | end 39 | 40 | def empty_fixtures 41 | FileUtils.rm_rf Dir.glob("#{fixture_folder}/*", File::FNM_DOTMATCH) 42 | end 43 | -------------------------------------------------------------------------------- /spec/projections_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Projectionist::Projections do 4 | let!(:old_cwd) { Dir.pwd } 5 | 6 | before do 7 | Dir.mkdir fixture_folder unless Dir.exist? fixture_folder 8 | write_fixtures('*/*' => { 'type' => 'test' }) 9 | Dir.chdir fixture_folder 10 | end 11 | 12 | after { Dir.chdir old_cwd } 13 | 14 | it('exists') { is_expected.not_to be nil } 15 | 16 | describe '.types' do 17 | before do 18 | subject.load_file fixture_file 19 | end 20 | 21 | it 'loads the list of types' do 22 | expect(subject.types).to eq ['test'] 23 | end 24 | 25 | it 'has a method to check if a given type exists' do 26 | expect(subject.type? 'test').to be true 27 | expect(subject.type? 'toast').to be false 28 | end 29 | 30 | it 'sets a flag that the file existed' do 31 | expect(subject.json_file_existed).to be true 32 | end 33 | end 34 | 35 | describe '#load_file' do 36 | context 'when in a child directory' do 37 | before do 38 | write_fixtures('*/*' => { 'type' => 'test' }) 39 | dir = File.join(fixture_folder, 'otherdir') 40 | Dir.mkdir dir unless Dir.exist? dir 41 | Dir.chdir dir 42 | subject = Projectionist::Projections.new 43 | subject.load_file 44 | end 45 | 46 | it 'still loads the file' do 47 | expect(subject.types).to eq ['test'] 48 | end 49 | 50 | it 'still sets a flag that the file existed' do 51 | expect(subject.json_file_existed).to be true 52 | end 53 | end 54 | 55 | context 'without a config file' do 56 | before do 57 | Dir.chdir fixture_folder 58 | delete_fixtures 59 | subject = Projectionist::Projections.new 60 | subject.load_file 61 | end 62 | 63 | it "sets a flag that the file didn't exist" do 64 | expect(subject.json_file_existed).to be false 65 | end 66 | end 67 | 68 | context 'with `**/*` in projections' do 69 | before { write_fixtures('test/**/*.rb' => { 'type' => 'disallowed' }) } 70 | it 'raises an error' do 71 | expect { subject.load_file }.to raise_error Projectionist::ProjectionError 72 | end 73 | end 74 | end 75 | 76 | describe '#files_for' do 77 | let(:test_dir) { File.join(fixture_folder, 'test') } 78 | let(:test_files) do 79 | (1..10).map { |n| File.join(test_dir, "#{n}_test.rb") } 80 | end 81 | let(:test_filenames) { (1..10).map(&:to_s) } 82 | 83 | before do 84 | write_fixtures('test/*_test.rb' => { 'type' => 'test' }) 85 | Dir.mkdir(test_dir) unless Dir.exist? test_dir 86 | 87 | test_files.each { |f| File.open(f, 'w') } 88 | 89 | # make bad test files as well 90 | 5.times do |n| 91 | file = File.join(test_dir, "#{n}_bad.rb") 92 | File.open(file, 'w') 93 | end 94 | 95 | subject.load_file 96 | end 97 | 98 | context 'with a valid type' do 99 | it 'matches the correct files' do 100 | expect(subject.files_for 'test').to match_array(test_filenames) 101 | end 102 | end 103 | 104 | context 'with verbose: true' do 105 | it 'returns the full filepaths' do 106 | expect(subject.files_for 'test', verbose: true) 107 | .to match_array(test_files) 108 | end 109 | end 110 | 111 | context 'without a valid type' do 112 | it 'returns an empty array' do 113 | expect(subject.files_for 'toast').to eq [] 114 | end 115 | end 116 | 117 | context 'with files in child directories' do 118 | let(:test_subdir) { File.join(test_dir, 'subdir') } 119 | let(:test_file_subdir) { File.join(test_subdir, 'subdir_test.rb') } 120 | let(:test_file_subdir_name) { 'subdir/subdir' } 121 | before do 122 | Dir.mkdir(test_subdir) unless Dir.exist? test_subdir 123 | File.open(test_file_subdir, 'w') 124 | end 125 | 126 | after { File.delete test_file_subdir } 127 | 128 | it 'includes the correct file' do 129 | expect(subject.files_for('test')).to include test_file_subdir_name 130 | end 131 | end 132 | end 133 | 134 | describe '#file_for' do 135 | context 'with simple globs' do 136 | let(:test_dir) { File.join(fixture_folder, 'test') } 137 | let(:test_file) { File.join(test_dir, 'file.rb') } 138 | before do 139 | write_fixtures('test/*.rb' => { 'type' => 'test' }) 140 | Dir.mkdir(test_dir) unless Dir.exist? test_dir 141 | File.open(test_file, 'w') 142 | Dir.chdir fixture_folder 143 | subject.load_file 144 | end 145 | 146 | context 'with a valid type' do 147 | it 'returns the correct file anyway' do 148 | expect(subject.file_for('test', 'file')).to eq test_file 149 | end 150 | end 151 | 152 | context 'without a valid type' do 153 | it 'returns nil' do 154 | expect(subject.file_for('toast', 'file')).to be_nil 155 | end 156 | end 157 | 158 | context "with a file that doesn't exist" do 159 | it 'returns the correct file' do 160 | expect(subject.file_for('test', 'nonexistent')).to eq File.join(test_dir, 'nonexistent.rb') 161 | end 162 | end 163 | 164 | context 'with other files in the directory' do 165 | let(:other_test_file) { File.join(test_dir, 'abc.rb') } 166 | before do 167 | File.open(other_test_file, 'w') 168 | end 169 | 170 | it 'matches the other file' do 171 | expect(subject.file_for('test', 'abc')).to eq other_test_file 172 | end 173 | 174 | it 'matches the old file' do 175 | expect(subject.file_for('test', 'file')).to eq test_file 176 | end 177 | end 178 | 179 | context 'with files in child directories' do 180 | let(:test_subdir) { File.join(test_dir, 'subdir') } 181 | let(:test_file_subdir) { File.join(test_subdir, 'file.rb') } 182 | before do 183 | Dir.mkdir(test_subdir) unless Dir.exist? test_subdir 184 | File.open(test_file_subdir, 'w') 185 | end 186 | 187 | it 'returns the correct file' do 188 | expect(subject.file_for('test', 'subdir/file')).to eq test_file_subdir 189 | end 190 | end 191 | 192 | context 'in a child directory' do 193 | before { Dir.chdir test_dir } 194 | it 'returns the correct file' do 195 | expect(subject.file_for 'test', 'file').to eq test_file 196 | end 197 | end 198 | end 199 | 200 | context 'with advanced globs' do 201 | let(:test_dir) { File.join(fixture_folder, 'test', 'foobar') } 202 | let(:test_file) { File.join(test_dir, 'test_file.rb') } 203 | before do 204 | write_fixtures('test/**/test_*.rb' => { 'type' => 'test' }) 205 | FileUtils.mkdir_p test_dir unless Dir.exist? test_dir 206 | File.open(test_file, 'w') 207 | Dir.chdir fixture_folder 208 | subject.load_file 209 | end 210 | 211 | it 'returns the correct file path' do 212 | expect(subject.file_for 'test', 'foobar/file').to eq test_file 213 | end 214 | end 215 | 216 | context 'with singleton projections' do 217 | let(:singleton_file) { File.join(fixture_folder, 'README.md') } 218 | before do 219 | write_fixtures('README.md' => { 'type' => 'readme' }) 220 | File.open(singleton_file, 'w') 221 | Dir.chdir fixture_folder 222 | subject.load_file 223 | end 224 | 225 | it 'returns the correct file path' do 226 | expect(subject.file_for 'readme').to eq singleton_file 227 | end 228 | end 229 | end 230 | end 231 | --------------------------------------------------------------------------------