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.
- data/.gitignore +9 -1
- data/.rspec +2 -1
- data/.tailor +6 -0
- data/Gemfile.lock +47 -78
- data/{ChangeLog.rdoc → History.rdoc} +0 -0
- data/README.rdoc +157 -24
- data/Rakefile +0 -9
- data/bin/tailor +16 -69
- data/features/configurable.feature +78 -0
- data/features/horizontal_spacing.feature +262 -0
- data/features/indentation.feature +17 -21
- data/features/indentation/bad_files_with_no_trailing_newline.feature +90 -0
- data/features/indentation/good_files_with_no_trailing_newline.feature +206 -0
- data/features/name_detection.feature +72 -0
- data/features/step_definitions/indentation_steps.rb +10 -133
- data/features/support/env.rb +7 -15
- data/features/support/file_cases/horizontal_spacing_cases.rb +265 -0
- data/features/support/file_cases/indentation_cases.rb +972 -0
- data/features/support/file_cases/naming_cases.rb +52 -0
- data/features/support/file_cases/vertical_spacing_cases.rb +70 -0
- data/features/support/hooks.rb +8 -0
- data/features/support/{1_file_with_bad_operator_spacing → legacy}/bad_op_spacing.rb +0 -0
- data/features/support/{1_file_with_bad_ternary_colon_spacing → legacy}/bad_ternary_colon_spacing.rb +0 -0
- data/features/support/{1_long_file_with_indentation/my_project.rb → legacy/long_file_with_indentation.rb} +1 -1
- data/features/support/world.rb +14 -0
- data/features/vertical_spacing.feature +114 -0
- data/lib/ext/string_ext.rb +5 -0
- data/lib/tailor.rb +6 -252
- data/lib/tailor/cli.rb +49 -0
- data/lib/tailor/cli/options.rb +251 -0
- data/lib/tailor/composite_observable.rb +56 -0
- data/lib/tailor/configuration.rb +263 -0
- data/lib/tailor/critic.rb +162 -0
- data/lib/tailor/formatters/text.rb +126 -0
- data/lib/tailor/lexed_line.rb +246 -0
- data/lib/tailor/lexer.rb +428 -0
- data/lib/tailor/lexer/token.rb +103 -0
- data/lib/tailor/lexer_constants.rb +75 -0
- data/lib/tailor/logger.rb +28 -0
- data/lib/tailor/problem.rb +100 -0
- data/lib/tailor/reporter.rb +48 -0
- data/lib/tailor/ruler.rb +39 -0
- data/lib/tailor/rulers.rb +7 -0
- data/lib/tailor/rulers/allow_camel_case_methods_ruler.rb +30 -0
- data/lib/tailor/rulers/allow_hard_tabs_ruler.rb +22 -0
- data/lib/tailor/rulers/allow_screaming_snake_case_classes_ruler.rb +32 -0
- data/lib/tailor/rulers/allow_trailing_line_spaces_ruler.rb +33 -0
- data/lib/tailor/rulers/indentation_spaces_ruler.rb +199 -0
- data/lib/tailor/rulers/indentation_spaces_ruler/indentation_manager.rb +362 -0
- data/lib/tailor/rulers/max_code_lines_in_class_ruler.rb +84 -0
- data/lib/tailor/rulers/max_code_lines_in_method_ruler.rb +84 -0
- data/lib/tailor/rulers/max_line_length_ruler.rb +31 -0
- data/lib/tailor/rulers/spaces_after_comma_ruler.rb +83 -0
- data/lib/tailor/rulers/spaces_after_lbrace_ruler.rb +114 -0
- data/lib/tailor/rulers/spaces_after_lbracket_ruler.rb +123 -0
- data/lib/tailor/rulers/spaces_after_lparen_ruler.rb +116 -0
- data/lib/tailor/rulers/spaces_before_comma_ruler.rb +67 -0
- data/lib/tailor/rulers/spaces_before_lbrace_ruler.rb +93 -0
- data/lib/tailor/rulers/spaces_before_rbrace_ruler.rb +98 -0
- data/lib/tailor/rulers/spaces_before_rbracket_ruler.rb +70 -0
- data/lib/tailor/rulers/spaces_before_rparen_ruler.rb +70 -0
- data/lib/tailor/rulers/spaces_in_empty_braces_ruler.rb +94 -0
- data/lib/tailor/rulers/trailing_newlines_ruler.rb +36 -0
- data/lib/tailor/runtime_error.rb +3 -0
- data/lib/tailor/tailorrc.erb +88 -0
- data/lib/tailor/version.rb +2 -2
- data/spec/spec_helper.rb +7 -5
- data/spec/tailor/cli_spec.rb +94 -0
- data/spec/tailor/configuration_spec.rb +147 -0
- data/spec/tailor/critic_spec.rb +63 -0
- data/spec/tailor/lexed_line_spec.rb +569 -0
- data/spec/tailor/lexer/token_spec.rb +46 -0
- data/spec/tailor/lexer_spec.rb +181 -0
- data/spec/tailor/options_spec.rb +6 -0
- data/spec/tailor/problem_spec.rb +74 -0
- data/spec/tailor/reporter_spec.rb +53 -0
- data/spec/tailor/ruler_spec.rb +56 -0
- data/spec/tailor/rulers/indentation_spaces_ruler/indentation_manager_spec.rb +454 -0
- data/spec/tailor/rulers/indentation_spaces_ruler_spec.rb +128 -0
- data/spec/tailor/rulers/spaces_after_comma_spec.rb +31 -0
- data/spec/tailor/rulers/spaces_after_lbrace_ruler_spec.rb +145 -0
- data/spec/tailor/rulers/spaces_before_lbrace_ruler_spec.rb +63 -0
- data/spec/tailor/rulers/spaces_before_rbrace_ruler_spec.rb +63 -0
- data/spec/tailor/rulers_spec.rb +9 -0
- data/spec/tailor/version_spec.rb +6 -0
- data/spec/tailor_spec.rb +9 -21
- data/tailor.gemspec +22 -35
- data/tasks/features.rake +7 -0
- data/tasks/roodi.rake +9 -0
- data/tasks/roodi_config.yaml +14 -0
- data/tasks/spec.rake +16 -0
- data/tasks/yard.rake +14 -0
- metadata +224 -77
- data/features/case_checking.feature +0 -38
- data/features/spacing.feature +0 -97
- data/features/spacing/commas.feature +0 -44
- data/features/step_definitions/case_checking_steps.rb +0 -42
- data/features/step_definitions/spacing_steps.rb +0 -156
- data/features/support/1_file_with_bad_comma_spacing/bad_comma_spacing.rb +0 -43
- data/features/support/1_file_with_bad_curly_brace_spacing/bad_curly_brace_spacing.rb +0 -60
- data/features/support/1_file_with_bad_parenthesis/bad_parenthesis.rb +0 -4
- data/features/support/1_file_with_bad_square_brackets/bad_square_brackets.rb +0 -62
- data/features/support/1_file_with_camel_case_class/camel_case_class.rb +0 -5
- data/features/support/1_file_with_camel_case_method/camel_case_method.rb +0 -3
- data/features/support/1_file_with_hard_tabs/hard_tab.rb +0 -3
- data/features/support/1_file_with_long_lines/long_lines.rb +0 -5
- data/features/support/1_file_with_snake_case_class/snake_case_class.rb +0 -5
- data/features/support/1_file_with_snake_case_method/snake_case_method.rb +0 -3
- data/features/support/1_file_with_trailing_whitespace/trailing_whitespace.rb +0 -5
- data/features/support/1_good_simple_file/simple_project.rb +0 -5
- data/features/support/common.rb +0 -102
- data/features/support/matchers.rb +0 -11
- data/lib/tailor/file_line.rb +0 -220
- data/lib/tailor/indentation.rb +0 -245
- data/lib/tailor/spacing.rb +0 -237
- data/spec/file_line_spec.rb +0 -70
- data/spec/indentation_spec.rb +0 -259
- data/spec/spacing/colon_spacing_spec.rb +0 -71
- data/spec/spacing/comma_spacing_spec.rb +0 -159
- data/spec/spacing/curly_brace_spacing_spec.rb +0 -257
- data/spec/spacing/parentheses_spacing_spec.rb +0 -28
- data/spec/spacing/square_bracket_spacing_spec.rb +0 -116
- data/spec/spacing_spec.rb +0 -167
- data/tasks/metrics.rake +0 -23
data/lib/tailor/cli.rb
ADDED
@@ -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
|