annotaterb 4.0.0.beta.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +0 -0
  3. data/LICENSE.txt +55 -0
  4. data/README.md +91 -0
  5. data/VERSION +1 -0
  6. data/exe/annotaterb +21 -0
  7. data/lib/annotate_rb/active_record_patch.rb +9 -0
  8. data/lib/annotate_rb/commands/annotate_models.rb +22 -0
  9. data/lib/annotate_rb/commands/annotate_routes.rb +19 -0
  10. data/lib/annotate_rb/commands/print_help.rb +16 -0
  11. data/lib/annotate_rb/commands/print_version.rb +12 -0
  12. data/lib/annotate_rb/commands.rb +10 -0
  13. data/lib/annotate_rb/config_finder.rb +21 -0
  14. data/lib/annotate_rb/config_loader.rb +63 -0
  15. data/lib/annotate_rb/core.rb +23 -0
  16. data/lib/annotate_rb/eager_loader.rb +23 -0
  17. data/lib/annotate_rb/env.rb +30 -0
  18. data/lib/annotate_rb/model_annotator/annotation_pattern_generator.rb +19 -0
  19. data/lib/annotate_rb/model_annotator/annotator.rb +74 -0
  20. data/lib/annotate_rb/model_annotator/bad_model_file_error.rb +11 -0
  21. data/lib/annotate_rb/model_annotator/constants.rb +22 -0
  22. data/lib/annotate_rb/model_annotator/file_annotation_remover.rb +25 -0
  23. data/lib/annotate_rb/model_annotator/file_annotator.rb +79 -0
  24. data/lib/annotate_rb/model_annotator/file_name_resolver.rb +16 -0
  25. data/lib/annotate_rb/model_annotator/file_patterns.rb +129 -0
  26. data/lib/annotate_rb/model_annotator/helper.rb +54 -0
  27. data/lib/annotate_rb/model_annotator/model_class_getter.rb +63 -0
  28. data/lib/annotate_rb/model_annotator/model_file_annotator.rb +118 -0
  29. data/lib/annotate_rb/model_annotator/model_files_getter.rb +62 -0
  30. data/lib/annotate_rb/model_annotator/pattern_getter.rb +27 -0
  31. data/lib/annotate_rb/model_annotator/schema_info.rb +480 -0
  32. data/lib/annotate_rb/model_annotator.rb +20 -0
  33. data/lib/annotate_rb/options.rb +204 -0
  34. data/lib/annotate_rb/parser.rb +385 -0
  35. data/lib/annotate_rb/rake_bootstrapper.rb +34 -0
  36. data/lib/annotate_rb/route_annotator/annotation_processor.rb +56 -0
  37. data/lib/annotate_rb/route_annotator/annotator.rb +40 -0
  38. data/lib/annotate_rb/route_annotator/base_processor.rb +104 -0
  39. data/lib/annotate_rb/route_annotator/header_generator.rb +113 -0
  40. data/lib/annotate_rb/route_annotator/helper.rb +104 -0
  41. data/lib/annotate_rb/route_annotator/removal_processor.rb +40 -0
  42. data/lib/annotate_rb/route_annotator.rb +12 -0
  43. data/lib/annotate_rb/runner.rb +34 -0
  44. data/lib/annotate_rb/tasks/annotate_models_migrate.rake +30 -0
  45. data/lib/annotate_rb.rb +30 -0
  46. data/lib/generators/annotate_rb/USAGE +4 -0
  47. data/lib/generators/annotate_rb/install_generator.rb +15 -0
  48. data/lib/generators/annotate_rb/templates/auto_annotate_models.rake +7 -0
  49. metadata +96 -0
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module ModelAnnotator
5
+ class FileNameResolver
6
+ class << self
7
+ def call(filename_template, model_name, table_name)
8
+ filename_template
9
+ .gsub('%MODEL_NAME%', model_name)
10
+ .gsub('%PLURALIZED_MODEL_NAME%', model_name.pluralize)
11
+ .gsub('%TABLE_NAME%', table_name || model_name.pluralize)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,129 @@
1
+ module AnnotateRb
2
+ module ModelAnnotator
3
+ # This module provides module method to get file paths.
4
+ module FilePatterns
5
+ # Controller files
6
+ CONTROLLER_DIR = File.join('app', 'controllers')
7
+
8
+ # Active admin registry files
9
+ ACTIVEADMIN_DIR = File.join('app', 'admin')
10
+
11
+ # Helper files
12
+ HELPER_DIR = File.join('app', 'helpers')
13
+
14
+ # File.join for windows reverse bar compat?
15
+ # I dont use windows, can`t test
16
+ UNIT_TEST_DIR = File.join('test', 'unit')
17
+ MODEL_TEST_DIR = File.join('test', 'models') # since rails 4.0
18
+ SPEC_MODEL_DIR = File.join('spec', 'models')
19
+
20
+ FIXTURE_TEST_DIR = File.join('test', 'fixtures')
21
+ FIXTURE_SPEC_DIR = File.join('spec', 'fixtures')
22
+
23
+ # Other test files
24
+ CONTROLLER_TEST_DIR = File.join('test', 'controllers')
25
+ CONTROLLER_SPEC_DIR = File.join('spec', 'controllers')
26
+ REQUEST_SPEC_DIR = File.join('spec', 'requests')
27
+ ROUTING_SPEC_DIR = File.join('spec', 'routing')
28
+
29
+ # Object Daddy http://github.com/flogic/object_daddy/tree/master
30
+ EXEMPLARS_TEST_DIR = File.join('test', 'exemplars')
31
+ EXEMPLARS_SPEC_DIR = File.join('spec', 'exemplars')
32
+
33
+ # Machinist http://github.com/notahat/machinist
34
+ BLUEPRINTS_TEST_DIR = File.join('test', 'blueprints')
35
+ BLUEPRINTS_SPEC_DIR = File.join('spec', 'blueprints')
36
+
37
+ # Factory Bot https://github.com/thoughtbot/factory_bot
38
+ FACTORY_BOT_TEST_DIR = File.join('test', 'factories')
39
+ FACTORY_BOT_SPEC_DIR = File.join('spec', 'factories')
40
+
41
+ # Fabrication https://github.com/paulelliott/fabrication.git
42
+ FABRICATORS_TEST_DIR = File.join('test', 'fabricators')
43
+ FABRICATORS_SPEC_DIR = File.join('spec', 'fabricators')
44
+
45
+ # Serializers https://github.com/rails-api/active_model_serializers
46
+ SERIALIZERS_DIR = File.join('app', 'serializers')
47
+ SERIALIZERS_TEST_DIR = File.join('test', 'serializers')
48
+ SERIALIZERS_SPEC_DIR = File.join('spec', 'serializers')
49
+
50
+ class << self
51
+ def generate(root_directory, pattern_type, options)
52
+ case pattern_type
53
+ when 'test' then test_files(root_directory)
54
+ when 'fixture' then fixture_files(root_directory)
55
+ when 'scaffold' then scaffold_files(root_directory)
56
+ when 'factory' then factory_files(root_directory)
57
+ when 'serializer' then serialize_files(root_directory)
58
+ when 'additional_file_patterns'
59
+ [options[:additional_file_patterns] || []].flatten
60
+ when 'controller'
61
+ [File.join(root_directory, CONTROLLER_DIR, '%PLURALIZED_MODEL_NAME%_controller.rb')]
62
+ when 'admin'
63
+ [
64
+ File.join(root_directory, ACTIVEADMIN_DIR, '%MODEL_NAME%.rb'),
65
+ File.join(root_directory, ACTIVEADMIN_DIR, '%PLURALIZED_MODEL_NAME%.rb')
66
+ ]
67
+ when 'helper'
68
+ [File.join(root_directory, HELPER_DIR, '%PLURALIZED_MODEL_NAME%_helper.rb')]
69
+ else
70
+ []
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def test_files(root_directory)
77
+ [
78
+ File.join(root_directory, UNIT_TEST_DIR, '%MODEL_NAME%_test.rb'),
79
+ File.join(root_directory, MODEL_TEST_DIR, '%MODEL_NAME%_test.rb'),
80
+ File.join(root_directory, SPEC_MODEL_DIR, '%MODEL_NAME%_spec.rb')
81
+ ]
82
+ end
83
+
84
+ def fixture_files(root_directory)
85
+ [
86
+ File.join(root_directory, FIXTURE_TEST_DIR, '%TABLE_NAME%.yml'),
87
+ File.join(root_directory, FIXTURE_SPEC_DIR, '%TABLE_NAME%.yml'),
88
+ File.join(root_directory, FIXTURE_TEST_DIR, '%PLURALIZED_MODEL_NAME%.yml'),
89
+ File.join(root_directory, FIXTURE_SPEC_DIR, '%PLURALIZED_MODEL_NAME%.yml')
90
+ ]
91
+ end
92
+
93
+ def scaffold_files(root_directory)
94
+ [
95
+ File.join(root_directory, CONTROLLER_TEST_DIR, '%PLURALIZED_MODEL_NAME%_controller_test.rb'),
96
+ File.join(root_directory, CONTROLLER_SPEC_DIR, '%PLURALIZED_MODEL_NAME%_controller_spec.rb'),
97
+ File.join(root_directory, REQUEST_SPEC_DIR, '%PLURALIZED_MODEL_NAME%_spec.rb'),
98
+ File.join(root_directory, ROUTING_SPEC_DIR, '%PLURALIZED_MODEL_NAME%_routing_spec.rb')
99
+ ]
100
+ end
101
+
102
+ def factory_files(root_directory)
103
+ [
104
+ File.join(root_directory, EXEMPLARS_TEST_DIR, '%MODEL_NAME%_exemplar.rb'),
105
+ File.join(root_directory, EXEMPLARS_SPEC_DIR, '%MODEL_NAME%_exemplar.rb'),
106
+ File.join(root_directory, BLUEPRINTS_TEST_DIR, '%MODEL_NAME%_blueprint.rb'),
107
+ File.join(root_directory, BLUEPRINTS_SPEC_DIR, '%MODEL_NAME%_blueprint.rb'),
108
+ File.join(root_directory, FACTORY_BOT_TEST_DIR, '%MODEL_NAME%_factory.rb'), # (old style)
109
+ File.join(root_directory, FACTORY_BOT_SPEC_DIR, '%MODEL_NAME%_factory.rb'), # (old style)
110
+ File.join(root_directory, FACTORY_BOT_TEST_DIR, '%TABLE_NAME%.rb'), # (new style)
111
+ File.join(root_directory, FACTORY_BOT_SPEC_DIR, '%TABLE_NAME%.rb'), # (new style)
112
+ File.join(root_directory, FACTORY_BOT_TEST_DIR, '%PLURALIZED_MODEL_NAME%.rb'), # (new style)
113
+ File.join(root_directory, FACTORY_BOT_SPEC_DIR, '%PLURALIZED_MODEL_NAME%.rb'), # (new style)
114
+ File.join(root_directory, FABRICATORS_TEST_DIR, '%MODEL_NAME%_fabricator.rb'),
115
+ File.join(root_directory, FABRICATORS_SPEC_DIR, '%MODEL_NAME%_fabricator.rb')
116
+ ]
117
+ end
118
+
119
+ def serialize_files(root_directory)
120
+ [
121
+ File.join(root_directory, SERIALIZERS_DIR, '%MODEL_NAME%_serializer.rb'),
122
+ File.join(root_directory, SERIALIZERS_TEST_DIR, '%MODEL_NAME%_serializer_test.rb'),
123
+ File.join(root_directory, SERIALIZERS_SPEC_DIR, '%MODEL_NAME%_serializer_spec.rb')
124
+ ]
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module ModelAnnotator
5
+ module Helper
6
+ MATCHED_TYPES = %w(test fixture factory serializer scaffold controller helper).freeze
7
+
8
+ class << self
9
+ def matched_types(options)
10
+ types = MATCHED_TYPES.dup
11
+ types << 'admin' if options[:active_admin] =~ Constants::TRUE_RE && !types.include?('admin')
12
+ types << 'additional_file_patterns' if options[:additional_file_patterns].present?
13
+
14
+ types
15
+ end
16
+
17
+ def magic_comments_as_string(content)
18
+ magic_comments = content.scan(Annotator::MAGIC_COMMENT_MATCHER).flatten.compact
19
+
20
+ if magic_comments.any?
21
+ magic_comments.join
22
+ else
23
+ ''
24
+ end
25
+ end
26
+
27
+ def skip_on_migration?
28
+ Env.read('ANNOTATE_SKIP_ON_DB_MIGRATE') =~ Constants::TRUE_RE || Env.read('skip_on_db_migrate') =~ Constants::TRUE_RE
29
+ end
30
+
31
+ def include_routes?
32
+ Env.read('routes') =~ Constants::TRUE_RE
33
+ end
34
+
35
+ def include_models?
36
+ Env.read('models') =~ Constants::TRUE_RE
37
+ end
38
+
39
+ def true?(val)
40
+ val.present? && Constants::TRUE_RE.match?(val)
41
+ end
42
+
43
+ # TODO: Find another implementation that doesn't depend on ActiveSupport
44
+ def fallback(*args)
45
+ args.compact.detect(&:present?)
46
+ end
47
+
48
+ def reset_options(options)
49
+ options.flatten.each { |key| Env.write(key, nil) }
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module ModelAnnotator
5
+ class ModelClassGetter
6
+ class << self
7
+ # Retrieve the classes belonging to the model names we're asked to process
8
+ # Check for namespaced models in subdirectories as well as models
9
+ # in subdirectories without namespacing.
10
+ def call(file, options)
11
+ model_path = file.gsub(/\.rb$/, '')
12
+ options[:model_dir].each { |dir| model_path = model_path.gsub(/^#{dir}/, '').gsub(/^\//, '') }
13
+
14
+ begin
15
+ get_loaded_model(model_path, file) || raise(BadModelFileError.new)
16
+ rescue LoadError
17
+ # this is for non-rails projects, which don't get Rails auto-require magic
18
+ file_path = File.expand_path(file)
19
+ if File.file?(file_path) && Kernel.require(file_path)
20
+ retry
21
+ elsif model_path =~ /\//
22
+ model_path = model_path.split('/')[1..-1].join('/').to_s
23
+ retry
24
+ else
25
+ raise
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ # Retrieve loaded model class
33
+ def get_loaded_model(model_path, file)
34
+ loaded_model_class = get_loaded_model_by_path(model_path)
35
+ return loaded_model_class if loaded_model_class
36
+
37
+ # We cannot get loaded model when `model_path` is loaded by Rails
38
+ # auto_load/eager_load paths. Try all possible model paths one by one.
39
+ absolute_file = File.expand_path(file)
40
+ model_paths =
41
+ $LOAD_PATH.select { |path| absolute_file.include?(path) }
42
+ .map { |path| absolute_file.sub(path, '').sub(/\.rb$/, '').sub(/^\//, '') }
43
+ model_paths
44
+ .map { |path| get_loaded_model_by_path(path) }
45
+ .find { |loaded_model| !loaded_model.nil? }
46
+ end
47
+
48
+ # Retrieve loaded model class by path to the file where it's supposed to be defined.
49
+ def get_loaded_model_by_path(model_path)
50
+ ::ActiveSupport::Inflector.constantize(::ActiveSupport::Inflector.camelize(model_path))
51
+ rescue StandardError, LoadError
52
+ # Revert to the old way but it is not really robust
53
+ ObjectSpace.each_object(::Class)
54
+ .select do |c|
55
+ Class === c && # note: we use === to avoid a bug in activesupport 2.3.14 OptionMerger vs. is_a?
56
+ c.ancestors.respond_to?(:include?) && # to fix FactoryGirl bug, see https://github.com/ctran/annotate_models/pull/82
57
+ c.ancestors.include?(::ActiveRecord::Base)
58
+ end.detect { |c| ::ActiveSupport::Inflector.underscore(c.to_s) == model_path }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module ModelAnnotator
5
+ # Not sure yet what the difference is between this and FileAnnotator
6
+ class ModelFileAnnotator
7
+ class << self
8
+ def call(annotated, file, header, options)
9
+ begin
10
+ return false if /#{Constants::SKIP_ANNOTATION_PREFIX}.*/ =~ (File.exist?(file) ? File.read(file) : '')
11
+ klass = ModelClassGetter.call(file, options)
12
+
13
+ klass_is_a_class = klass.is_a?(Class)
14
+ klass_inherits_active_record_base = klass < ActiveRecord::Base
15
+ klass_is_not_abstract = !klass.abstract_class?
16
+ klass_table_exists = klass.table_exists?
17
+
18
+ not_sure_this_conditional = (!options[:exclude_sti_subclasses] || !(klass.superclass < ActiveRecord::Base && klass.table_name == klass.superclass.table_name))
19
+
20
+ annotate_conditions = [
21
+ klass_is_a_class,
22
+ klass_inherits_active_record_base,
23
+ not_sure_this_conditional,
24
+ klass_is_not_abstract,
25
+ klass_table_exists
26
+ ]
27
+
28
+ do_annotate = annotate_conditions.all?
29
+
30
+ if do_annotate
31
+ files_annotated = annotate(klass, file, header, options)
32
+ annotated.concat(files_annotated)
33
+ end
34
+
35
+ rescue BadModelFileError => e
36
+ unless options[:ignore_unknown_models]
37
+ $stderr.puts "Unable to annotate #{file}: #{e.message}"
38
+ $stderr.puts "\t" + e.backtrace.join("\n\t") if options[:trace]
39
+ end
40
+ rescue StandardError => e
41
+ $stderr.puts "Unable to annotate #{file}: #{e.message}"
42
+ $stderr.puts "\t" + e.backtrace.join("\n\t") if options[:trace]
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # Given the name of an ActiveRecord class, create a schema
49
+ # info block (basically a comment containing information
50
+ # on the columns and their types) and put it at the front
51
+ # of the model and fixture source files.
52
+ #
53
+ # === Options (opts)
54
+ # :position_in_class<Symbol>:: where to place the annotated section in model file
55
+ # :position_in_test<Symbol>:: where to place the annotated section in test/spec file(s)
56
+ # :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
57
+ # :position_in_factory<Symbol>:: where to place the annotated section in factory file
58
+ # :position_in_serializer<Symbol>:: where to place the annotated section in serializer file
59
+ # :exclude_tests<Symbol>:: whether to skip modification of test/spec files
60
+ # :exclude_fixtures<Symbol>:: whether to skip modification of fixture files
61
+ # :exclude_factories<Symbol>:: whether to skip modification of factory files
62
+ # :exclude_serializers<Symbol>:: whether to skip modification of serializer files
63
+ # :exclude_scaffolds<Symbol>:: whether to skip modification of scaffold files
64
+ # :exclude_controllers<Symbol>:: whether to skip modification of controller files
65
+ # :exclude_helpers<Symbol>:: whether to skip modification of helper files
66
+ # :exclude_sti_subclasses<Symbol>:: whether to skip modification of files for STI subclasses
67
+ #
68
+ # == Returns:
69
+ # an array of file names that were annotated.
70
+ #
71
+ def annotate(klass, file, header, options = {})
72
+ begin
73
+ klass.reset_column_information
74
+ info = SchemaInfo.generate(klass, header, options)
75
+ model_name = klass.name.underscore
76
+ table_name = klass.table_name
77
+ model_file_name = File.join(file)
78
+ annotated = []
79
+
80
+ if FileAnnotator.call(model_file_name, info, :position_in_class, options)
81
+ annotated << model_file_name
82
+ end
83
+
84
+ Helper.matched_types(options).each do |key|
85
+ exclusion_key = "exclude_#{key.pluralize}".to_sym
86
+ position_key = "position_in_#{key}".to_sym
87
+
88
+ # Same options for active_admin models
89
+ if key == 'admin'
90
+ exclusion_key = 'exclude_class'.to_sym
91
+ position_key = 'position_in_class'.to_sym
92
+ end
93
+
94
+ next if options[exclusion_key]
95
+
96
+ patterns = PatternGetter.call(options, key)
97
+
98
+ patterns
99
+ .map { |f| FileNameResolver.call(f, model_name, table_name) }
100
+ .map { |f| Dir.glob(f) }
101
+ .flatten
102
+ .each do |f|
103
+ if FileAnnotator.call(f, info, position_key, options)
104
+ annotated << f
105
+ end
106
+ end
107
+ end
108
+ rescue StandardError => e
109
+ $stderr.puts "Unable to annotate #{file}: #{e.message}"
110
+ $stderr.puts "\t" + e.backtrace.join("\n\t") if options[:trace]
111
+ end
112
+
113
+ annotated
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module ModelAnnotator
5
+ class ModelFilesGetter
6
+ class << self
7
+ # Return a list of the model files to annotate.
8
+ # If we have command line arguments, they're assumed to the path
9
+ # of model files from root dir. Otherwise we take all the model files
10
+ # in the model_dir directory.
11
+ def call(options)
12
+ model_files = []
13
+
14
+ model_files = list_model_files_from_argument(options) if !options[:is_rake]
15
+
16
+ return model_files if !model_files.empty?
17
+
18
+ options[:model_dir].each do |dir|
19
+ Dir.chdir(dir) do
20
+ list = if options[:ignore_model_sub_dir]
21
+ Dir["*.rb"].map { |f| [dir, f] }
22
+ else
23
+ Dir["**/*.rb"].reject { |f| f["concerns/"] }.map { |f| [dir, f] }
24
+ end
25
+ model_files.concat(list)
26
+ end
27
+ end
28
+
29
+ model_files
30
+ rescue SystemCallError
31
+ $stderr.puts "No models found in directory '#{options[:model_dir].join("', '")}'."
32
+ $stderr.puts "Either specify models on the command line, or use the --model-dir option."
33
+ $stderr.puts "Call 'annotate --help' for more info."
34
+ # exit 1 # TODO: Return exit code back to caller. Right now it messes up RSpec being able to run
35
+ end
36
+
37
+ private
38
+
39
+ def list_model_files_from_argument(options)
40
+ return [] if ARGV.empty?
41
+
42
+ specified_files = ARGV.map { |file| File.expand_path(file) }
43
+
44
+ model_files = options[:model_dir].flat_map do |dir|
45
+ absolute_dir_path = File.expand_path(dir)
46
+ specified_files
47
+ .find_all { |file| file.start_with?(absolute_dir_path) }
48
+ .map { |file| [dir, file.sub("#{absolute_dir_path}/", '')] }
49
+ end
50
+
51
+ if model_files.size != specified_files.size
52
+ $stderr.puts "The specified file could not be found in directory '#{options[:model_dir].join("', '")}'."
53
+ $stderr.puts "Call 'annotate --help' for more info."
54
+ # exit 1 # TODO: Return exit code back to caller. Right now it messes up RSpec being able to run
55
+ end
56
+
57
+ model_files
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnnotateRb
4
+ module ModelAnnotator
5
+ class PatternGetter
6
+ class << self
7
+ def call(options, pattern_types = [])
8
+ current_patterns = []
9
+
10
+ options[:root_dir].each do |root_directory|
11
+ Array(pattern_types).each do |pattern_type|
12
+ patterns = FilePatterns.generate(root_directory, pattern_type, options)
13
+
14
+ current_patterns += if pattern_type.to_sym == :additional_file_patterns
15
+ patterns
16
+ else
17
+ patterns.map { |p| p.sub(/^[\/]*/, '') }
18
+ end
19
+ end
20
+ end
21
+
22
+ current_patterns
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end