tailor 0.1.5 → 1.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|