diff --git a/.gitignore b/.gitignore index 9ceb501..e55d85a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ pkg/ tmp/ */.DS_Store +*.swp diff --git a/lib/tailor/cli/options.rb b/lib/tailor/cli/options.rb index e7c50d5..42b4408 100644 --- a/lib/tailor/cli/options.rb +++ b/lib/tailor/cli/options.rb @@ -77,6 +77,11 @@ def self.parse!(args) options.style[:max_line_length] = c end + opt.on('--quotes type', + 'Type of quotes that should be used (default: off)') do |c| + options.style[:quotes] = c + end + opt.on('--spaces-after-comma NUMBER', 'Spaces to expect after a comma. (default: 1)') do |c| options.style[:spaces_after_comma] = c diff --git a/lib/tailor/configuration/style.rb b/lib/tailor/configuration/style.rb index 304b7e2..d133a4d 100644 --- a/lib/tailor/configuration/style.rb +++ b/lib/tailor/configuration/style.rb @@ -30,6 +30,7 @@ def self.define_property(name) define_property :allow_screaming_snake_case_classes define_property :allow_trailing_line_spaces define_property :allow_invalid_ruby + define_property :quotes define_property :indentation_spaces define_property :max_code_lines_in_class define_property :max_code_lines_in_method @@ -53,6 +54,7 @@ def initialize allow_screaming_snake_case_classes(false, level: :error) allow_trailing_line_spaces(false, level: :error) allow_invalid_ruby(false, level: :warn) + quotes("off", level: :error) indentation_spaces(2, level: :error) max_code_lines_in_class(300, level: :error) max_code_lines_in_method(30, level: :error) diff --git a/lib/tailor/critic.rb b/lib/tailor/critic.rb index 61a3adf..f2dc446 100644 --- a/lib/tailor/critic.rb +++ b/lib/tailor/critic.rb @@ -94,7 +94,9 @@ def init_rulers(style, lexer, parent_ruler) end log "Initializing ruler: #{ruler}" - ruler = instance_eval("#{ruler}.new(#{values.first}, #{values.last})") + first_argument, second_argument = values.first, values.last + first_argument = "'#{first_argument}'" if first_argument.is_a? String + ruler = instance_eval("#{ruler}.new(#{first_argument}, #{second_argument})") parent_ruler.add_child_ruler(ruler) ruler.lexer_observers.each do |observer| diff --git a/lib/tailor/lexer.rb b/lib/tailor/lexer.rb index e293a98..e3aa38b 100644 --- a/lib/tailor/lexer.rb +++ b/lib/tailor/lexer.rb @@ -455,7 +455,7 @@ def on_tstring_beg(token) log "TSTRING_BEG: '#{token}'" current_line = LexedLine.new(super, lineno) tstring_beg_changed - notify_tstring_beg_observers(current_line, lineno) + notify_tstring_beg_observers(current_line, lineno, column, token) super(token) end diff --git a/lib/tailor/rulers/indentation_spaces_ruler.rb b/lib/tailor/rulers/indentation_spaces_ruler.rb index 7e7f207..26dd6a4 100644 --- a/lib/tailor/rulers/indentation_spaces_ruler.rb +++ b/lib/tailor/rulers/indentation_spaces_ruler.rb @@ -229,7 +229,7 @@ def rparen_update(current_lexed_line, lineno, column) @manager.update_for_closing_reason(:on_rparen, current_lexed_line) end - def tstring_beg_update(lexed_line, lineno) + def tstring_beg_update(lexed_line, lineno, column, token) @tstring_nesting << lineno @manager.update_actual_indentation(lexed_line) log "tstring_nesting is now: #{@tstring_nesting}" diff --git a/lib/tailor/rulers/quotes_ruler.rb b/lib/tailor/rulers/quotes_ruler.rb new file mode 100644 index 0000000..85642e1 --- /dev/null +++ b/lib/tailor/rulers/quotes_ruler.rb @@ -0,0 +1,33 @@ +require_relative '../ruler' + +class Tailor + module Rulers + class QuotesRuler < Tailor::Ruler + def initialize(config, options) + super(config, options) + add_lexer_observers :tstring_beg + end + + def tstring_beg_update(lexed_line, lineno, column, token) + log "<#{self.class}> Line length: #{lexed_line.line_length}" + measure(lexed_line, lineno, column, token) + end + + # Checks to see if the usage of quotes is consistent and as expected by +@config+. + # + # @param [Fixnum] lexed_line The line to measure. + # @param [Fixnum] lineno Line the potential problem is on. + # @param [Fixnum] column Column the potential problem is on + # @param [Fixnum] token Begin quote string + def measure(lexed_line, lineno, column, token) + spec = { "single" => "\"", "double" => "'" } + if spec.has_key? @config and token == spec[@config] + msg = "Use #{@config} quotes" + + @problems << Problem.new(problem_type, lineno, column, msg, + @options[:level]) + end + end + end + end +end diff --git a/lib/tailor/tailorrc.erb b/lib/tailor/tailorrc.erb index 92662c0..961861f 100644 --- a/lib/tailor/tailorrc.erb +++ b/lib/tailor/tailorrc.erb @@ -79,6 +79,13 @@ # the file. # Default: 1 # +#------------------------------------------------------------------------------ +# Other +#------------------------------------------------------------------------------ +# quotes Type of the quotes that should be used consistently +# across a whole project ("single" or "double") +# Default: off +# Tailor.config do |config| config.formatters "<%= formatters.join(", ") %>" config.file_set '<%= file_list %>' do |style|<% style.each do |rule, value| %> diff --git a/spec/functional/quotes_spec.rb b/spec/functional/quotes_spec.rb new file mode 100644 index 0000000..b1060eb --- /dev/null +++ b/spec/functional/quotes_spec.rb @@ -0,0 +1,107 @@ +require_relative '../spec_helper' +require 'tailor/critic' +require 'tailor/configuration/style' + +describe "Consistent usage of quotes" do + before do + Tailor::Logger.stub(:log) + FakeFS.activate! + File.open(file_name.to_s, 'w') { |f| f.write contents } + critic.check_file(file_name.to_s, style.to_hash) + end + + let(:critic) do + Tailor::Critic.new + end + + let(:contents) { + { + :single_quotes => %Q{a = 'test'}, + :double_quotes => %Q{a = "test"}, + :mixed_quotes => %Q{a = 'test'; b = "test"}, + :special_quotes => %Q{a = %q(test); b = %Q(test)} + }[file_name] + } + + + describe "single quotes" do + let(:style) do + style = Tailor::Configuration::Style.new + style.trailing_newlines 0, level: :off + style.allow_invalid_ruby true, level: :off + style.quotes "single", level: :error + style + end + + context "ok" do + let(:file_name) { :single_quotes } + + specify { critic.problems[file_name.to_s].size.should == 0 } + end + context "double quotes" do + let(:file_name) { :double_quotes } + + specify { critic.problems[file_name.to_s].size.should == 1 } + specify { critic.problems[file_name.to_s].first[:type].should == "quotes" } + specify { critic.problems[file_name.to_s].first[:line].should be 1 } + specify { critic.problems[file_name.to_s].first[:column].should be 4 } + specify { critic.problems[file_name.to_s].first[:level].should be :error } + end + context "mixed quotes" do + let(:file_name) { :mixed_quotes } + + specify { critic.problems[file_name.to_s].size.should == 1 } + specify { critic.problems[file_name.to_s].first[:type].should == "quotes" } + specify { critic.problems[file_name.to_s].first[:line].should be 1 } + specify { critic.problems[file_name.to_s].first[:column].should be 16 } + specify { critic.problems[file_name.to_s].first[:level].should be :error } + end + context "special quotes" do + let(:file_name) { :special_quotes } + + specify { critic.problems[file_name.to_s].size.should == 0 } + end + end + + describe "double quotes" do + let(:style) do + style = Tailor::Configuration::Style.new + style.trailing_newlines 0, level: :off + style.allow_invalid_ruby true, level: :off + style.quotes "double", level: :error + style + end + + context "ok" do + let(:file_name) { :double_quotes } + + specify { critic.problems[file_name.to_s].size.should == 0 } + end + + context "single quotes" do + let(:file_name) { :single_quotes } + + specify { critic.problems[file_name.to_s].size.should == 1 } + specify { critic.problems[file_name.to_s].first[:type].should == "quotes" } + specify { critic.problems[file_name.to_s].first[:line].should be 1 } + specify { critic.problems[file_name.to_s].first[:column].should be 4 } + specify { critic.problems[file_name.to_s].first[:level].should be :error } + end + + context "mixed quotes" do + let(:file_name) { :mixed_quotes } + + specify { critic.problems[file_name.to_s].size.should == 1 } + specify { critic.problems[file_name.to_s].first[:type].should == "quotes" } + specify { critic.problems[file_name.to_s].first[:line].should be 1 } + specify { critic.problems[file_name.to_s].first[:column].should be 4 } + specify { critic.problems[file_name.to_s].first[:level].should be :error } + end + + context "special quotes" do + let(:file_name) { :special_quotes } + + specify { critic.problems[file_name.to_s].size.should == 0 } + end + end +end diff --git a/spec/unit/tailor/configuration/style_spec.rb b/spec/unit/tailor/configuration/style_spec.rb index 3c97ab1..2fe592b 100644 --- a/spec/unit/tailor/configuration/style_spec.rb +++ b/spec/unit/tailor/configuration/style_spec.rb @@ -165,6 +165,7 @@ :max_code_lines_in_class => [300, { :level => :error }], :max_code_lines_in_method => [30, { :level => :error }], :max_line_length => [80, { :level => :error }], + :quotes => ["off", { :level => :error }], :spaces_after_comma => [1, { :level => :error }], :spaces_after_lbrace => [1, { :level => :error }], :spaces_after_lbracket => [0, { :level => :error }], diff --git a/spec/unit/tailor/configuration_spec.rb b/spec/unit/tailor/configuration_spec.rb index 97e7639..535b661 100644 --- a/spec/unit/tailor/configuration_spec.rb +++ b/spec/unit/tailor/configuration_spec.rb @@ -47,6 +47,7 @@ max_code_lines_in_class: [300, { level: :error }], max_code_lines_in_method: [30, { level: :error }], max_line_length: [80, { level: :error }], + quotes: ["off", { level: :error }], spaces_after_comma: [1, { level: :error }], spaces_after_lbrace: [1, { level: :error }], spaces_after_lbracket: [0, { level: :error }], diff --git a/spec/unit/tailor/rulers/indentation_spaces_ruler_spec.rb b/spec/unit/tailor/rulers/indentation_spaces_ruler_spec.rb index b983a99..8353524 100644 --- a/spec/unit/tailor/rulers/indentation_spaces_ruler_spec.rb +++ b/spec/unit/tailor/rulers/indentation_spaces_ruler_spec.rb @@ -98,14 +98,14 @@ manager.should_receive(:update_actual_indentation).with lexed_line manager.should_receive(:stop) subject.instance_variable_set(:@manager, manager) - subject.tstring_beg_update(lexed_line, 1) + subject.tstring_beg_update(lexed_line, 1, nil, nil) end it "adds the lineno to @tstring_nesting" do manager.stub(:update_actual_indentation) manager.stub(:stop) subject.instance_variable_set(:@manager, manager) - subject.tstring_beg_update(lexed_line, 1) + subject.tstring_beg_update(lexed_line, 1, nil, nil) subject.instance_variable_get(:@tstring_nesting).should == [1] end end