tailor 0.1.5 → 1.0.0.alpha

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 (124) hide show
  1. data/.gitignore +9 -1
  2. data/.rspec +2 -1
  3. data/.tailor +6 -0
  4. data/Gemfile.lock +47 -78
  5. data/{ChangeLog.rdoc → History.rdoc} +0 -0
  6. data/README.rdoc +157 -24
  7. data/Rakefile +0 -9
  8. data/bin/tailor +16 -69
  9. data/features/configurable.feature +78 -0
  10. data/features/horizontal_spacing.feature +262 -0
  11. data/features/indentation.feature +17 -21
  12. data/features/indentation/bad_files_with_no_trailing_newline.feature +90 -0
  13. data/features/indentation/good_files_with_no_trailing_newline.feature +206 -0
  14. data/features/name_detection.feature +72 -0
  15. data/features/step_definitions/indentation_steps.rb +10 -133
  16. data/features/support/env.rb +7 -15
  17. data/features/support/file_cases/horizontal_spacing_cases.rb +265 -0
  18. data/features/support/file_cases/indentation_cases.rb +972 -0
  19. data/features/support/file_cases/naming_cases.rb +52 -0
  20. data/features/support/file_cases/vertical_spacing_cases.rb +70 -0
  21. data/features/support/hooks.rb +8 -0
  22. data/features/support/{1_file_with_bad_operator_spacing → legacy}/bad_op_spacing.rb +0 -0
  23. data/features/support/{1_file_with_bad_ternary_colon_spacing → legacy}/bad_ternary_colon_spacing.rb +0 -0
  24. data/features/support/{1_long_file_with_indentation/my_project.rb → legacy/long_file_with_indentation.rb} +1 -1
  25. data/features/support/world.rb +14 -0
  26. data/features/vertical_spacing.feature +114 -0
  27. data/lib/ext/string_ext.rb +5 -0
  28. data/lib/tailor.rb +6 -252
  29. data/lib/tailor/cli.rb +49 -0
  30. data/lib/tailor/cli/options.rb +251 -0
  31. data/lib/tailor/composite_observable.rb +56 -0
  32. data/lib/tailor/configuration.rb +263 -0
  33. data/lib/tailor/critic.rb +162 -0
  34. data/lib/tailor/formatters/text.rb +126 -0
  35. data/lib/tailor/lexed_line.rb +246 -0
  36. data/lib/tailor/lexer.rb +428 -0
  37. data/lib/tailor/lexer/token.rb +103 -0
  38. data/lib/tailor/lexer_constants.rb +75 -0
  39. data/lib/tailor/logger.rb +28 -0
  40. data/lib/tailor/problem.rb +100 -0
  41. data/lib/tailor/reporter.rb +48 -0
  42. data/lib/tailor/ruler.rb +39 -0
  43. data/lib/tailor/rulers.rb +7 -0
  44. data/lib/tailor/rulers/allow_camel_case_methods_ruler.rb +30 -0
  45. data/lib/tailor/rulers/allow_hard_tabs_ruler.rb +22 -0
  46. data/lib/tailor/rulers/allow_screaming_snake_case_classes_ruler.rb +32 -0
  47. data/lib/tailor/rulers/allow_trailing_line_spaces_ruler.rb +33 -0
  48. data/lib/tailor/rulers/indentation_spaces_ruler.rb +199 -0
  49. data/lib/tailor/rulers/indentation_spaces_ruler/indentation_manager.rb +362 -0
  50. data/lib/tailor/rulers/max_code_lines_in_class_ruler.rb +84 -0
  51. data/lib/tailor/rulers/max_code_lines_in_method_ruler.rb +84 -0
  52. data/lib/tailor/rulers/max_line_length_ruler.rb +31 -0
  53. data/lib/tailor/rulers/spaces_after_comma_ruler.rb +83 -0
  54. data/lib/tailor/rulers/spaces_after_lbrace_ruler.rb +114 -0
  55. data/lib/tailor/rulers/spaces_after_lbracket_ruler.rb +123 -0
  56. data/lib/tailor/rulers/spaces_after_lparen_ruler.rb +116 -0
  57. data/lib/tailor/rulers/spaces_before_comma_ruler.rb +67 -0
  58. data/lib/tailor/rulers/spaces_before_lbrace_ruler.rb +93 -0
  59. data/lib/tailor/rulers/spaces_before_rbrace_ruler.rb +98 -0
  60. data/lib/tailor/rulers/spaces_before_rbracket_ruler.rb +70 -0
  61. data/lib/tailor/rulers/spaces_before_rparen_ruler.rb +70 -0
  62. data/lib/tailor/rulers/spaces_in_empty_braces_ruler.rb +94 -0
  63. data/lib/tailor/rulers/trailing_newlines_ruler.rb +36 -0
  64. data/lib/tailor/runtime_error.rb +3 -0
  65. data/lib/tailor/tailorrc.erb +88 -0
  66. data/lib/tailor/version.rb +2 -2
  67. data/spec/spec_helper.rb +7 -5
  68. data/spec/tailor/cli_spec.rb +94 -0
  69. data/spec/tailor/configuration_spec.rb +147 -0
  70. data/spec/tailor/critic_spec.rb +63 -0
  71. data/spec/tailor/lexed_line_spec.rb +569 -0
  72. data/spec/tailor/lexer/token_spec.rb +46 -0
  73. data/spec/tailor/lexer_spec.rb +181 -0
  74. data/spec/tailor/options_spec.rb +6 -0
  75. data/spec/tailor/problem_spec.rb +74 -0
  76. data/spec/tailor/reporter_spec.rb +53 -0
  77. data/spec/tailor/ruler_spec.rb +56 -0
  78. data/spec/tailor/rulers/indentation_spaces_ruler/indentation_manager_spec.rb +454 -0
  79. data/spec/tailor/rulers/indentation_spaces_ruler_spec.rb +128 -0
  80. data/spec/tailor/rulers/spaces_after_comma_spec.rb +31 -0
  81. data/spec/tailor/rulers/spaces_after_lbrace_ruler_spec.rb +145 -0
  82. data/spec/tailor/rulers/spaces_before_lbrace_ruler_spec.rb +63 -0
  83. data/spec/tailor/rulers/spaces_before_rbrace_ruler_spec.rb +63 -0
  84. data/spec/tailor/rulers_spec.rb +9 -0
  85. data/spec/tailor/version_spec.rb +6 -0
  86. data/spec/tailor_spec.rb +9 -21
  87. data/tailor.gemspec +22 -35
  88. data/tasks/features.rake +7 -0
  89. data/tasks/roodi.rake +9 -0
  90. data/tasks/roodi_config.yaml +14 -0
  91. data/tasks/spec.rake +16 -0
  92. data/tasks/yard.rake +14 -0
  93. metadata +224 -77
  94. data/features/case_checking.feature +0 -38
  95. data/features/spacing.feature +0 -97
  96. data/features/spacing/commas.feature +0 -44
  97. data/features/step_definitions/case_checking_steps.rb +0 -42
  98. data/features/step_definitions/spacing_steps.rb +0 -156
  99. data/features/support/1_file_with_bad_comma_spacing/bad_comma_spacing.rb +0 -43
  100. data/features/support/1_file_with_bad_curly_brace_spacing/bad_curly_brace_spacing.rb +0 -60
  101. data/features/support/1_file_with_bad_parenthesis/bad_parenthesis.rb +0 -4
  102. data/features/support/1_file_with_bad_square_brackets/bad_square_brackets.rb +0 -62
  103. data/features/support/1_file_with_camel_case_class/camel_case_class.rb +0 -5
  104. data/features/support/1_file_with_camel_case_method/camel_case_method.rb +0 -3
  105. data/features/support/1_file_with_hard_tabs/hard_tab.rb +0 -3
  106. data/features/support/1_file_with_long_lines/long_lines.rb +0 -5
  107. data/features/support/1_file_with_snake_case_class/snake_case_class.rb +0 -5
  108. data/features/support/1_file_with_snake_case_method/snake_case_method.rb +0 -3
  109. data/features/support/1_file_with_trailing_whitespace/trailing_whitespace.rb +0 -5
  110. data/features/support/1_good_simple_file/simple_project.rb +0 -5
  111. data/features/support/common.rb +0 -102
  112. data/features/support/matchers.rb +0 -11
  113. data/lib/tailor/file_line.rb +0 -220
  114. data/lib/tailor/indentation.rb +0 -245
  115. data/lib/tailor/spacing.rb +0 -237
  116. data/spec/file_line_spec.rb +0 -70
  117. data/spec/indentation_spec.rb +0 -259
  118. data/spec/spacing/colon_spacing_spec.rb +0 -71
  119. data/spec/spacing/comma_spacing_spec.rb +0 -159
  120. data/spec/spacing/curly_brace_spacing_spec.rb +0 -257
  121. data/spec/spacing/parentheses_spacing_spec.rb +0 -28
  122. data/spec/spacing/square_bracket_spacing_spec.rb +0 -116
  123. data/spec/spacing_spec.rb +0 -167
  124. data/tasks/metrics.rake +0 -23
@@ -0,0 +1,49 @@
1
+ require_relative 'configuration'
2
+ require_relative 'critic'
3
+ require_relative 'cli/options'
4
+ require_relative 'logger'
5
+ require_relative 'reporter'
6
+
7
+ class Tailor
8
+
9
+ # The Command-Line Interface worker. Execution from the command line should
10
+ # come through here.
11
+ class CLI
12
+ include LogSwitch::Mixin
13
+
14
+ # The main method of execution from the command line.
15
+ def self.run(args)
16
+ new(args).execute!
17
+ end
18
+
19
+ def initialize(args)
20
+ Tailor::Logger.log = false
21
+ options = Options.parse!(args)
22
+
23
+ @configuration = Configuration.new(args, options)
24
+ @configuration.load!
25
+
26
+ if options.show_config
27
+ @configuration.show
28
+ end
29
+
30
+ @critic = Critic.new(@configuration.file_sets)
31
+ @reporter = Reporter.new(@configuration.formatters)
32
+ end
33
+
34
+ # This checks all of the files detected during the configuration gathering
35
+ # process, then hands results over to the {Reporter} to be reported.
36
+ #
37
+ # @return [Boolean] +true+ if no problems were detected; false if there
38
+ # were.
39
+ def execute!
40
+ @critic.critique do |problems_for_file, label|
41
+ @reporter.file_report(problems_for_file, label)
42
+ end
43
+
44
+ @reporter.summary_report(@critic.problems)
45
+
46
+ @critic.problem_count > 0
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,251 @@
1
+ require 'erb'
2
+ require 'optparse'
3
+ require 'ostruct'
4
+ require 'text-table'
5
+ require_relative '../version'
6
+ require_relative '../configuration'
7
+
8
+ class Tailor
9
+ class CLI
10
+ class Options
11
+ @output_color = true
12
+
13
+ def self.parse!(args)
14
+ options = OpenStruct.new
15
+ options.config_file = ''
16
+ options.formatters = []
17
+ options.show_config = false
18
+ options.style = {}
19
+
20
+ opts = OptionParser.new do |opt|
21
+ opt.banner = self.banner
22
+
23
+ opt.separator ""
24
+ opt.separator ""
25
+ opt.separator "Config file options:"
26
+ opt.on('-s', '--show-config', 'Show your current config.') do
27
+ options.show_config = true
28
+ end
29
+
30
+ opt.on('-c', '--config-file FILE',
31
+ "Use a specific config file.") do |config|
32
+ options.config_file = config
33
+ end
34
+
35
+ opt.on('--create-config', 'Create a new .tailor file') do
36
+ if create_config
37
+ msg = "Your new tailor config file was created at "
38
+ msg << "#{Dir.pwd}."
39
+ $stdout.puts msg
40
+ exit
41
+ else
42
+ $stderr.puts "Creation of .tailor failed!"
43
+ exit 1
44
+ end
45
+ end
46
+
47
+ #---------------------------------------------------------------------
48
+ # Style options
49
+ #---------------------------------------------------------------------
50
+ opt.separator ""
51
+ opt.separator "Style Options:"
52
+ opt.separator " (Any option that doesn't have an explicit way of"
53
+ opt.separator " turning it off can be done so simply by passing"
54
+ opt.separator " passing it 'false'.)"
55
+
56
+ opt.separator ""
57
+ opt.separator " * Horizontal Spacing:"
58
+ opt.on('--allow-hard-tabs BOOL',
59
+ 'Check for hard tabs? (default: true)') do |c|
60
+ options.style[:allow_hard_tabs] = c
61
+ end
62
+
63
+ opt.on('--allow-trailing-line-spaces BOOL',
64
+ 'Check for trailing spaces at the end of lines?',
65
+ '(default: true)') do |c|
66
+ options.style[:allow_trailing_line_spaces] = c
67
+ end
68
+
69
+ opt.on('--indentation-spaces NUMBER',
70
+ 'Spaces to expect indentation. (default: 2)') do |c|
71
+ options.style[:indentation_spaces] = c
72
+ end
73
+
74
+ opt.on('--max-line-length NUMBER',
75
+ 'Max characters in a line. (default: 80)') do |c|
76
+ options.style[:max_line_length] = c
77
+ end
78
+
79
+ opt.on('--spaces-after-comma NUMBER',
80
+ 'Spaces to expect after a comma. (default: 1)') do |c|
81
+ options.style[:spaces_after_comma] = c
82
+ end
83
+
84
+ opt.on('--spaces-before-comma NUMBER',
85
+ 'Spaces to expect before a comma. (default: 0)') do |c|
86
+ options.style[:spaces_before_comma] = c
87
+ end
88
+
89
+ opt.on('--spaces-after-lbrace NUMBER',
90
+ 'Spaces to expect after a {. (default: 1)') do |c|
91
+ options.style[:spaces_after_lbrace] = c
92
+ end
93
+
94
+ opt.on('--spaces-before-lbrace NUMBER',
95
+ 'Spaces to expect before a {. (default: 1)') do |c|
96
+ options.style[:spaces_before_lbrace] = c
97
+ end
98
+
99
+ opt.on('--spaces-before-rbrace NUMBER', Integer,
100
+ 'Spaces to expect before a }. (default: 1)') do |c|
101
+ options.style[:spaces_before_rbrace] = c
102
+ end
103
+
104
+ opt.on('--spaces-in-empty-braces NUMBER', Integer,
105
+ 'Spaces to expect between a { and }. (default: 0)') do |c|
106
+ options.style[:spaces_in_empty_braces] = c
107
+ end
108
+
109
+ opt.on('--spaces-after-lbracket NUMBER', Integer,
110
+ 'Spaces to expect after a [. (default: 0)') do |c|
111
+ options.style[:spaces_after_lbracket] = c
112
+ end
113
+
114
+ opt.on('--spaces-before-rbracket NUMBER', Integer,
115
+ 'Spaces to expect before a ]. (default: 0)') do |c|
116
+ options.style[:spaces_before_rbracket] = c
117
+ end
118
+
119
+ opt.on('--spaces-after-lparen NUMBER', Integer,
120
+ 'Spaces to expect after a (. (default: 0)') do |c|
121
+ options.style[:spaces_after_lparen] = c
122
+ end
123
+
124
+ opt.on('--spaces-before-rparen NUMBER', Integer,
125
+ 'Spaces to expect before a ). (default: 0)') do |c|
126
+ options.style[:spaces_before_rparen] = c
127
+ end
128
+
129
+ opt.separator ""
130
+ opt.separator ""
131
+
132
+ opt.separator " * Naming:"
133
+
134
+ opt.on('--allow-camel-case-methods BOOL',
135
+ 'Check for camel-case method names?', '(default: true)') do |c|
136
+ options.style[:allow_camel_case_methods] = instance_eval(c)
137
+ end
138
+
139
+ opt.on('--allow-screaming-snake-case-classes BOOL',
140
+ 'Check for classes like "My_Class"?', '(default: true)') do |c|
141
+ options.style[:allow_screaming_snake_case_classes] =
142
+ instance_eval(c)
143
+ end
144
+
145
+ opt.separator ""
146
+ opt.separator ""
147
+ opt.separator " * Vertical Spacing"
148
+
149
+ opt.on('--max-code-lines-in-class NUMBER', Integer,
150
+ 'Max number lines of code in a class.', '(default: 300)') do |c|
151
+ options.style[:max_code_lines_in_class] = c
152
+ end
153
+
154
+ opt.on('--max-code-lines-in-method NUMBER', Integer,
155
+ 'Max number lines of code in a method.', '(default: 30)') do |c|
156
+ options.style[:max_code_lines_in_method] = c
157
+ end
158
+
159
+ opt.on('--trailing-newlines NUMBER', Integer,
160
+ 'Newlines to expect at the end of the file.', '(default: 1)') do |c|
161
+ options.style[:trailing_newlines] = c
162
+ end
163
+
164
+ #---------------------------------------------------------------------
165
+ # Common options
166
+ #---------------------------------------------------------------------
167
+ opt.separator ""
168
+ opt.separator "Common options:"
169
+
170
+ opt.on('-f', '--format FORMATTER') do |format|
171
+ options.formatters << format
172
+ end
173
+
174
+ opt.on('--[no-]color', "Output in color") do |color|
175
+ @output_color = color
176
+ end
177
+
178
+ opt.on_tail('-v', '--version', "Show the version") do
179
+ puts version
180
+ exit
181
+ end
182
+
183
+ opt.on_tail('-d', '--debug', "Turn on debug logging") do
184
+ Tailor::Logger.log = true
185
+ end
186
+
187
+ opt.on_tail('-h', '--help', 'Show this message') do |help|
188
+ puts opt
189
+ exit
190
+ end
191
+ end
192
+
193
+ opts.parse!(args)
194
+ require_relative '../../ext/string_ext' if @output_color
195
+
196
+ options
197
+ end
198
+
199
+ # @return [String]
200
+ def self.banner
201
+ ruler + about + "\r\n" + usage + "\r\n"
202
+ end
203
+
204
+ # @return [String]
205
+ def self.version
206
+ ruler + about + "\r\n"
207
+ end
208
+
209
+ # @return [String]
210
+ def self.ruler
211
+ <<-RULER
212
+ _________________________________________________________________________
213
+ | | | | | | | | | | | | | | | | | | | | | | | | |
214
+ | | | | | | | | | | | | |
215
+ | | | | | | |
216
+ | 1 2 3 4 5 |
217
+ | |
218
+ -------------------------------------------------------------------------
219
+ RULER
220
+ end
221
+
222
+ # @return [String]
223
+ def self.about
224
+ <<-ABOUT
225
+ tailor (v#{Tailor::VERSION}). \t\tA Ruby style checker.
226
+ \t\t\t\t\thttp://github.com/turboladen/tailor
227
+ ABOUT
228
+ end
229
+
230
+ # @return [String]
231
+ def self.usage
232
+ " Usage: tailor [FILE|DIR]"
233
+ end
234
+
235
+ def self.create_config
236
+ if File.exists? Dir.pwd + '/.tailor'
237
+ $stderr.puts "Can't create new config; it already exists."
238
+ false
239
+ else
240
+ erb_file = File.expand_path(
241
+ File.dirname(__FILE__) + '/../tailorrc.erb')
242
+ formatters = Tailor::Configuration.default.formatters
243
+ file_list = 'lib/**/*.rb'
244
+ style = Tailor::Configuration.default.file_sets[:default][:style]
245
+ default_config_file = ERB.new(File.read(erb_file)).result(binding)
246
+ File.open('.tailor', 'w') { |f| f.write default_config_file }
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,56 @@
1
+ class Tailor
2
+ module CompositeObservable
3
+ def self.define_observer(name)
4
+ define_method("add_#{name}_observer") do |observer|
5
+ @notifiers = {} unless defined? @notifiers
6
+ @notifiers[name] = {} unless @notifiers[name]
7
+
8
+ call_back = "#{name}_update".to_sym
9
+
10
+ unless observer.respond_to? call_back
11
+ raise NoMethodError, "observer does not respond to '#{call_back}'"
12
+ end
13
+
14
+ @notifiers[name][observer] = call_back
15
+ end
16
+
17
+ define_method("notify_#{name}_observers") do |*args|
18
+ if defined? @notifier_state and @notifier_state
19
+ if @notifier_state[name]
20
+ if defined? @notifiers and @notifiers[name]
21
+ @notifiers[name].each do |k, v|
22
+ k.send(v, *args)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ define_method("#{name}_changed") do
30
+ @notifier_state = {} unless defined? @notifier_state
31
+ @notifier_state[name] = true
32
+ end
33
+ end
34
+
35
+ define_observer :comma
36
+ define_observer :comment
37
+ define_observer :const
38
+ define_observer :embexpr_beg
39
+ define_observer :embexpr_end
40
+ define_observer :file
41
+ define_observer :ident
42
+ define_observer :ignored_nl
43
+ define_observer :kw
44
+ define_observer :lbrace
45
+ define_observer :lbracket
46
+ define_observer :lparen
47
+ define_observer :nl
48
+ define_observer :period
49
+ define_observer :rbrace
50
+ define_observer :rbracket
51
+ define_observer :rparen
52
+ define_observer :sp
53
+ define_observer :tstring_beg
54
+ define_observer :tstring_end
55
+ end
56
+ end
@@ -0,0 +1,263 @@
1
+ require_relative '../tailor'
2
+ require_relative 'runtime_error'
3
+ require_relative 'logger'
4
+
5
+ class Tailor
6
+
7
+ # Pulls in any configuration from the places configuration can be set:
8
+ # 1. ~/.tailorrc
9
+ # 2. CLI options
10
+ # 3. Default options
11
+ #
12
+ # It then basically represents a list of "file sets" and the rulers that
13
+ # should be applied against each file set.
14
+ class Configuration
15
+ include Tailor::Logger::Mixin
16
+
17
+ DEFAULT_GLOB = 'lib/**/*.rb'
18
+ DEFAULT_RC_FILE = Dir.home + '/.tailorrc'
19
+ DEFAULT_PROJECT_CONFIG = Dir.pwd + '/.tailor'
20
+ DEFAULT_STYLE = {
21
+ allow_camel_case_methods: false,
22
+ allow_hard_tabs: false,
23
+ allow_screaming_snake_case_classes: false,
24
+ allow_trailing_line_spaces: false,
25
+ indentation_spaces: 2,
26
+ max_code_lines_in_class: 300,
27
+ max_code_lines_in_method: 30,
28
+ max_line_length: 80,
29
+ spaces_after_comma: 1,
30
+ spaces_before_comma: 0,
31
+ spaces_before_lbrace: 1,
32
+ spaces_after_lbrace: 1,
33
+ spaces_before_rbrace: 1,
34
+ spaces_in_empty_braces: 0,
35
+ spaces_after_lbracket: 0,
36
+ spaces_before_rbracket: 0,
37
+ spaces_after_lparen: 0,
38
+ spaces_before_rparen: 0,
39
+ trailing_newlines: 1
40
+ }
41
+
42
+ # @return [Hash]
43
+ def self.default
44
+ new
45
+ end
46
+
47
+ attr_reader :file_sets
48
+ attr_reader :formatters
49
+
50
+ # @param [Array] runtime_file_list
51
+ # @param [OpenStruct] options
52
+ # @option options [String] config_file
53
+ # @option options [Array] formatters
54
+ # @option options [Hash] style
55
+ def initialize(runtime_file_list=nil, options=nil)
56
+ @formatters = ['text']
57
+ @file_sets = {
58
+ default: {
59
+ file_list: file_list(DEFAULT_GLOB),
60
+ style: DEFAULT_STYLE
61
+ }
62
+ }
63
+
64
+ @runtime_file_list = runtime_file_list
65
+ log "Got runtime file list: #{@runtime_file_list}"
66
+ @options = options
67
+ log "Got options: #{@options}"
68
+ end
69
+
70
+ # Call this to load settings from the config file and from CLI options.
71
+ def load!
72
+ # Get config file settings
73
+ @config_file = @options.config_file unless @options.config_file.empty?
74
+ load_from_config_file(config_file)
75
+
76
+ if @config_file
77
+
78
+ if @rc_file_config
79
+ # Get formatters from config file
80
+ unless @rc_file_config.formatters.empty?
81
+ @formatters = @rc_file_config.formatters
82
+ log "@formatters is now #{@formatters}"
83
+ end
84
+
85
+ # Get file sets from config file
86
+ unless @rc_file_config.file_sets.empty?
87
+ @rc_file_config.file_sets.each do |label, file_set|
88
+ unless @file_sets[label]
89
+ @file_sets[label] = { style: DEFAULT_STYLE }
90
+ end
91
+
92
+ @file_sets[label][:file_list].concat file_set[:file_list]
93
+ @file_sets[label][:file_list].uniq!
94
+ @file_sets[label][:style].merge! file_set[:style]
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ # Get formatters from CLI options
101
+ unless @options.formatters.empty? || @options.formatters.nil?
102
+ @formatters = @options.formatters
103
+ log "@formatters is now #{@formatters}"
104
+ end
105
+
106
+ # Get file sets from CLI options
107
+ unless @runtime_file_list.nil? || @runtime_file_list.empty?
108
+ # Only use options set for the :default file set because the user gave
109
+ # a different set of files to measure.
110
+ @file_sets.delete_if { |k, v| k != :default }
111
+ @file_sets[:default][:file_list] = file_list(@runtime_file_list)
112
+ end
113
+
114
+ # Get style overrides from CLI options
115
+ @file_sets[:default][:style].merge!(@options.style)
116
+
117
+ if @file_sets[:default][:file_list].empty?
118
+ @file_sets[:default][:file_list] = file_list(DEFAULT_GLOB)
119
+ end
120
+
121
+ end
122
+
123
+ # @return [String] Name of the config file to use.
124
+ def config_file
125
+ return @config_file if @config_file
126
+
127
+ if File.exists?(DEFAULT_PROJECT_CONFIG)
128
+ return @config_file = DEFAULT_PROJECT_CONFIG
129
+ end
130
+
131
+ if File.exists?(DEFAULT_RC_FILE)
132
+ return @config_file = DEFAULT_RC_FILE
133
+ end
134
+ end
135
+
136
+ # @return [Array] The list of formatters.
137
+ def formatters(*new_formatters)
138
+ @formatters = new_formatters unless new_formatters.empty?
139
+
140
+ @formatters
141
+ end
142
+
143
+ # @param [String] file_glob The String that represents the file set. This
144
+ # can be a file, directory, or a glob.
145
+ # @param [Symbol] label The label that represents the file set.
146
+ def file_set(file_glob=DEFAULT_GLOB, label=:default, &block)
147
+ log "file set label #{label}"
148
+
149
+ @temp_style = {}
150
+ instance_eval(&block) if block_given?
151
+
152
+ if @file_sets[label]
153
+ @file_sets[label][:file_list].concat file_list(file_glob)
154
+ log "file set: #{@file_sets}"
155
+ @file_sets[label][:file_list].uniq!
156
+ @file_sets[label][:style].merge @temp_style
157
+ else
158
+ @file_sets[label] = {
159
+ file_list: file_list(file_glob),
160
+ style: DEFAULT_STYLE.merge(@temp_style)
161
+ }
162
+ end
163
+
164
+ @temp_style = {}
165
+ end
166
+
167
+ # Implemented for {file_set}, this converts the config file lines that look
168
+ # like methods into a Hash.
169
+ #
170
+ # @return [Hash] The new style as defined by the config file.
171
+ def method_missing(meth, *args, &blk)
172
+ ok_methods = DEFAULT_STYLE.keys
173
+
174
+ if ok_methods.include? meth
175
+ @temp_style[meth] = args.first
176
+ else
177
+ super(meth, args, blk)
178
+ end
179
+ end
180
+
181
+ # Tries to open the file at the path given at +config_file+ and read in
182
+ # the configuration given there.
183
+ #
184
+ # @param [String] config_file Path to the config file to use.
185
+ def load_from_config_file(config_file)
186
+ user_config_file = File.expand_path(config_file)
187
+
188
+ if File.exists? user_config_file
189
+ log "Loading config from file: #{user_config_file}"
190
+ begin
191
+ config = instance_eval File.read(user_config_file)
192
+ rescue LoadError => ex
193
+ raise Tailor::RuntimeError,
194
+ "Couldn't load config file: #{user_config_file}"
195
+ end
196
+ else
197
+ log "No config file found at #{user_config_file}."
198
+ end
199
+
200
+ if config
201
+ log "Got new config from file: #{config}"
202
+ @rc_file_config = config
203
+ end
204
+ end
205
+
206
+ # Gets a list of only files that are in +base_dir+.
207
+ #
208
+ # @param [String] base_dir The directory to get the file list for.
209
+ # @return [Array<String>] The List of files.
210
+ def all_files_in_dir(base_dir)
211
+ files = Dir.glob(File.join(base_dir, '**', '*')).find_all do |file|
212
+ file if File.file?(file)
213
+ end
214
+
215
+ files
216
+ end
217
+
218
+ # The list of the files in the project to check.
219
+ #
220
+ # @param [String] glob Path to the file, directory or glob to check.
221
+ # @return [Array] The list of files to check.
222
+ def file_list(glob)
223
+ files_in_project = if glob.is_a? Array
224
+ log "Configured glob is an Array: #{glob}"
225
+
226
+ glob.map do |e|
227
+ if File.directory?(e)
228
+ all_files_in_dir(e)
229
+ else
230
+ e
231
+ end
232
+ end.flatten.uniq
233
+ elsif File.directory? glob
234
+ log "Configured glob is an directory: #{glob}"
235
+ all_files_in_dir(glob)
236
+ else
237
+ log "Configured glob is a glob/single-file: #{glob}"
238
+ Dir.glob glob
239
+ end
240
+
241
+ list_with_absolute_paths = []
242
+
243
+ files_in_project.each do |file|
244
+ list_with_absolute_paths << File.expand_path(file)
245
+ end
246
+
247
+ log "All files: #{list_with_absolute_paths}"
248
+
249
+ list_with_absolute_paths.sort
250
+ end
251
+
252
+ def show
253
+ table = Text::Table.new(horizontal_padding: 4)
254
+ table.head = [{ value: 'Configuration', colspan: 2, align: :center }]
255
+ table.rows << :separator
256
+ table.rows << ['Style', @file_sets.inspect]
257
+ table.rows << :separator
258
+ table.rows << ['Formatters', @formatters]
259
+
260
+ puts table
261
+ end
262
+ end
263
+ end