syntaxer 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
2
+ pp File.join(File.dirname(__FILE__), '..', 'lib')
1
3
  require 'aruba/cucumber'
2
4
  require 'fileutils'
3
- require "git"
5
+ require "git"
6
+ require 'syntaxer'
7
+ require 'cucumber/rspec/doubles'
@@ -5,22 +5,28 @@ require "forwardable"
5
5
  require "git"
6
6
  require "rainbow"
7
7
  require 'progress_bar'
8
+ require 'highline'
9
+ require "highline/import"
8
10
  require File.join(%w{syntaxer reader})
9
11
  require File.join(%w{syntaxer file_status})
10
12
  require File.join(%w{syntaxer checker})
13
+ require File.join(%w{syntaxer runner})
11
14
  require File.join(%w{syntaxer repository})
12
15
  require File.join(%w{syntaxer language_definition})
13
16
  require File.join(%w{syntaxer printer})
14
17
  require File.join(%w{syntaxer progress_bar})
18
+ require File.join(%w{syntaxer wizzard})
19
+ require File.join(%w{syntaxer writer})
15
20
 
16
21
  require File.join(%w{syntaxer railtie}) if defined?(Rails)
17
22
 
18
23
  module Syntaxer
19
24
  DEFAULT_FILES_MASK = "**/*"
20
25
  SYNTAXER_RULES_FILE = File.join(File.dirname(__FILE__), "..", "syntaxer_rules.dist.rb")
26
+ SYNTAXER_CONFIG_FILE_NAME = "syntaxer.rb"
21
27
 
22
28
  class << self
23
- attr_reader :reader, :repository, :root_path, :results, :warnings, :hook
29
+ attr_reader :reader, :repository, :root_path, :results, :warnings, :hook, :jslint
24
30
 
25
31
  def configure
26
32
  yield(self) if block_given?
@@ -38,17 +44,27 @@ module Syntaxer
38
44
 
39
45
  def check_syntax(options = {})
40
46
  @root_path = options[:root_path]
41
- Printer.quite = options[:quite] || false
42
- Printer.loud = options[:loud] || false
43
47
  @warnings = options[:warnings]
44
48
  @hook = options[:hook]
49
+ @jslint = options[:jslint]
50
+ Printer.quite = options[:quite] || false
51
+ Printer.loud = options[:loud] || false
45
52
 
46
53
  @reader = Reader::DSLReader.load(options[:config_file])
54
+
55
+ if @jslint # if jslint option passed set from command line we have to add new rule with indicated dir
56
+ rule = LanguageDefinition.new(:javascript, %w{js}, nil, [@jslint+"*", @jslint+"**/*"], nil, nil, nil, true, true)
57
+ rule.exec_rule = Runner.javascript.call
58
+ @reader.add_rule rule
59
+ end
60
+
47
61
  @repository = Repository.factory(@root_path, options[:repository]) if options[:repository]
48
-
62
+
63
+ $stdmyout = StringIO.new
49
64
  checker = Checker.process(self)
50
65
  Printer.print_result checker
51
- exit(1) unless checker.error_files.empty?
66
+
67
+ exit(1) unless checker.error_files.empty? && $stdmyout.string.empty?
52
68
  end
53
69
 
54
70
  # This method generate and put hook to .git/hooks
@@ -63,20 +79,37 @@ module Syntaxer
63
79
  @root_path = options[:root_path]
64
80
  raise ArgumentError, 'Indicate repository type' unless options.include?(:repository)
65
81
  raise ArgumentError, "SVN is temporarily not supported" if options[:repository].to_sym == :svn
66
-
67
- repo = Repository.factory(@root_path, options[:repository])
82
+
68
83
  hook_file = "#{@root_path}/.git/hooks/pre-commit"
69
- hook_string = "syntaxer -r git --hook"
70
- hook_string += " -c config/syntaxer.rb" if options[:rails]
84
+ hook_string = 'syntaxer '
85
+
86
+ if options[:restore] && File.exist?(File.join(@root_path,'.syntaxer'))
87
+ hook_string += File.open(File.join(@root_path,'.syntaxer')).read
88
+ else
89
+ repo = Repository.factory(@root_path, options[:repository])
90
+ hook_string += "-r git --hook"
91
+ hook_string += " -c config/syntaxer.rb" if options[:rails]
92
+ hook_string += " -c #{options[:config_file]}" unless options[:config_file].nil?
93
+ end
71
94
 
72
95
  File.open(hook_file, 'w') do |f|
73
96
  f.puts hook_string
74
97
  end
75
98
  File.chmod(0755, hook_file)
99
+
100
+ # save syntaxer options
101
+ File.open(File.join(options[:root_path],'.syntaxer'), 'w') do |f|
102
+ f.write(hook_string.gsub('syntaxer ',''))
103
+ end
104
+
76
105
  rescue Exception => e
77
106
  puts e.message.color(:red)
78
107
  raise e
79
108
  end
109
+
110
+ def wizzard(options)
111
+ Wizzard.start
112
+ end
80
113
 
81
114
  end
82
115
  end
@@ -1,8 +1,9 @@
1
1
  require 'set'
2
2
  require "observer"
3
+ require 'thread'
3
4
  module Syntaxer
4
5
  class Checker
5
- include Open3
6
+ # include Open3
6
7
  include Observable
7
8
  extend Forwardable
8
9
 
@@ -43,20 +44,14 @@ module Syntaxer
43
44
  notify_observers({:rule => rule})
44
45
  else
45
46
  if @syntaxer.warnings && rule.name == :ruby
46
- rule.exec_rule = rule.exec_rule.gsub(/(-\S+)\s/,'\1w ')
47
+ rule.exec_rule.exec_rule = rule.exec_rule.exec_rule.gsub(/(-\S+)\s/,'\1w ')
47
48
  end
48
- errors = run_exec_rule(rule, file)
49
+ errors = rule.exec_rule.run(file)
49
50
  FileStatus.build(file, errors)
50
51
  notify_observers({:file_status => errors.empty?})
51
52
  end
52
53
  end
53
54
 
54
- def run_exec_rule rule, file
55
- popen3(rule.exec_rule.gsub('%filename%', file)) do |stdin, stdout, stderr, wait_thr|
56
- stderr.read.split("\n")
57
- end
58
- end
59
-
60
55
  end
61
56
 
62
57
  # Check status of files in repository
@@ -64,7 +59,27 @@ module Syntaxer
64
59
  class RepoChecker < Checker
65
60
 
66
61
  def initialize syntaxer
67
- super syntaxer, syntaxer.repository.changed_and_added_files.length
62
+ @syntaxer = syntaxer
63
+ count_of_files = 0
64
+ @rule_files = {}
65
+ @deferred_process = []
66
+ syntaxer.reader.rules.each do |rule|
67
+ @rule_files[rule.name] = {}
68
+ @rule_files[rule.name][:rule] = rule
69
+ @rule_files[rule.name][:files] = []
70
+ rule.extensions.each do |ext|
71
+ files.each do |file|
72
+ if File.extname(file).gsub(/\./,'') == ext || \
73
+ (!rule.specific_files.nil? && !@rule_files[rule.name][:files].include?(file) && rule.specific_files.include?(file))
74
+
75
+ @rule_files[rule.name][:files].push(file)
76
+ count_of_files += 1 if !rule.deferred # skip these files
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ super syntaxer, count_of_files
68
83
  end
69
84
 
70
85
  # Check syntax in repository directory
@@ -72,28 +87,19 @@ module Syntaxer
72
87
  # @see Checker#process
73
88
 
74
89
  def process
75
- checked_files = Set.new
76
- rule_files = {}
77
-
78
- @reader.rules.each do |rule|
79
- rule_files[rule.name] = {}
80
- rule_files[rule.name][:rule] = rule
81
- rule_files[rule.name][:files] = []
82
- rule.extensions.each do |ext|
83
- files.each do |file|
84
- if File.extname(file).gsub(/\./,'') == ext || \
85
- (!rule.specific_files.nil? && !rule_files[rule.name][:files].include?(file) && rule.specific_files.include?(file))
86
- rule_files[rule.name][:files].push(file)
87
- end
90
+ @rule_files.each do |rule_name, rule|
91
+ if rule[:rule].deferred
92
+ @deferred_process << rule
93
+ else
94
+ rule[:files].each do |file|
95
+ full_path = File.join(@syntaxer.root_path,file)
96
+ check(rule[:rule], full_path)
88
97
  end
89
98
  end
90
99
  end
91
100
 
92
- rule_files.each do |rule_name, rule|
93
- rule[:files].each do |file|
94
- full_path = File.join(@syntaxer.root_path,file)
95
- check(rule[:rule], full_path)
96
- end
101
+ @deferred_process.each do |rule|
102
+ rule[:rule].exec_rule.run(@syntaxer.root_path, rule[:files])
97
103
  end
98
104
 
99
105
  self
@@ -116,13 +122,21 @@ module Syntaxer
116
122
  # @see Checker#process
117
123
 
118
124
  def process
125
+ @deferred_process = []
119
126
  @reader.rules.each do |rule|
120
- # check if executor exists
121
- rule.files_list(@syntaxer.root_path).each do |file|
122
- check(rule, file)
127
+ if rule.deferred
128
+ @deferred_process << rule
129
+ else
130
+ rule.files_list(@syntaxer.root_path).each do |file|
131
+ check(rule, file)
132
+ end
123
133
  end
124
134
  end
125
135
 
136
+ @deferred_process.each do |rule|
137
+ rule.exec_rule.run(@syntaxer.root_path, rule.files_list(@syntaxer.root_path))
138
+ end
139
+
126
140
  self
127
141
  end
128
142
 
@@ -1,7 +1,7 @@
1
1
  module Syntaxer
2
2
  class LanguageDefinitionException < Exception; end
3
3
 
4
- LanguageDefinition = Struct.new(:name, :extensions, :specific_files, :folders, :ignore_folders, :exec_rule, :executor, :exec_existence) do
4
+ LanguageDefinition = Struct.new(:name, :extensions, :specific_files, :folders, :ignore_folders, :exec_rule, :executor, :exec_existence, :deferred) do
5
5
  def initialize(*args)
6
6
  super(*args)
7
7
  raise LanguageDefinitionException.new "name can't be blank" unless self.name
@@ -29,8 +29,6 @@ module Syntaxer
29
29
  return if @quite
30
30
  if args.include?(:file_status)
31
31
  @mode == :hook ? @@bar.increment!(args[:file_status]) : @@bar.increment!
32
- # @@bar.increment!(args[:file_status]) if @mode == :hook
33
- # @@bar.increment! unless @mode == :hook
34
32
  end
35
33
  true
36
34
  end
@@ -42,19 +40,21 @@ module Syntaxer
42
40
  def print_result checker
43
41
  return if @quite
44
42
  puts "\n"
45
- puts "Syntax OK".color(:green) if checker.error_files.empty?
43
+ puts "Syntax OK".color(:green) if checker.error_files.empty? && $stdmyout.string.empty?
46
44
 
47
45
  @loud ? (files = checker.all_files) : (files = checker.error_files)
48
46
  files.each do |file|
49
47
  print_message(file)
50
48
  end
51
-
49
+
52
50
  unless @@not_exists_rules.empty?
53
51
  puts "\n"
54
52
  @@not_exists_rules.each do |rule|
55
53
  puts (NON_EXISTENT_RULE_MESSAGE % [rule.executor, rule.name]).color(:yellow)
56
54
  end
57
55
  end
56
+
57
+ $stderr.puts("\nErrors:"+"\n\t"+$stdmyout.string.color(:red)) unless $stdmyout.string.empty?
58
58
  end
59
59
 
60
60
  def print_message filestatus
@@ -4,9 +4,18 @@ module Syntaxer
4
4
  if defined? Rails::Railtie
5
5
  require 'rails'
6
6
  class Railtie < Rails::Railtie
7
+
7
8
  rake_tasks do
8
9
  load "tasks/syntaxer.rake"
9
10
  end
11
+
12
+ initializer "syntaxer.initialize" do |app|
13
+ if app.root.join('.syntaxer').exist? && !app.root.join('.git/hooks/pre-commit').exist? && Rails.env.development?
14
+ Syntaxer.make_hook({:root_path => app.root, :repository => :git, :rails => true, :restore => true})
15
+ puts "Syntaxer hook restored.".color(:green)
16
+ end
17
+ end
18
+
10
19
  end
11
20
  end
12
21
  end
@@ -14,9 +14,18 @@ module Syntaxer
14
14
  @ignore_folders = []
15
15
  end
16
16
 
17
+ def add_rule rule
18
+ @rules << rule
19
+ self
20
+ end
21
+
17
22
  def files_count syntaxer
18
23
  @rules.map{ |rule|
19
- rule.files_list(syntaxer.root_path).length
24
+ if rule.deferred
25
+ 0 # skip such files
26
+ else
27
+ rule.files_list(syntaxer.root_path).length
28
+ end
20
29
  }.inject(:+)
21
30
  end
22
31
 
@@ -123,11 +132,25 @@ module Syntaxer
123
132
  def specific_files(*args)
124
133
  current_rule.specific_files = args
125
134
  end
126
-
135
+
136
+ # Create exec rule for language. It may be console string and
137
+ # ruby method
138
+ #
139
+ # @param [String|Proc]
127
140
  def exec_rule(exec_string)
128
- current_rule.executor = exec_string.scan(/\w+/).first
129
- current_rule.exec_existence = system("which #{current_rule.executor} > /dev/null")
130
- current_rule.exec_rule = exec_string
141
+ # if it is string create default console runner
142
+ if !exec_string.respond_to?(:call) || exec_string.is_a?(String)
143
+ current_rule.executor = exec_string.scan(/\w+/).first
144
+ current_rule.exec_existence = system("which #{current_rule.executor} > /dev/null")
145
+ exec_rule = Syntaxer::Runner.default(exec_string)
146
+ current_rule.deferred = false
147
+ else
148
+ # if it is proc call it and pass current rule
149
+ current_rule.exec_existence = true
150
+ current_rule.deferred = true # we have run it after all console checkers
151
+ exec_rule = exec_string.call
152
+ end
153
+ current_rule.exec_rule = exec_rule
131
154
  end
132
155
 
133
156
  def ignore_folders(ignore_folders)
@@ -0,0 +1,57 @@
1
+ require 'jslint'
2
+
3
+ module Syntaxer
4
+ class Runner
5
+ include Open3
6
+
7
+ attr_accessor :exec_rule, :language
8
+
9
+ def initialize exec_rule = nil
10
+ @exec_rule = exec_rule
11
+ end
12
+
13
+ class << self
14
+ def javascript
15
+ lambda do
16
+ c = Syntaxer::Runner.new
17
+ c.language = 'javascript' # it is using for backward compatibility
18
+ c.extend(Runners::Javascript)
19
+ c
20
+ end
21
+ end
22
+
23
+ def default exec_rule
24
+ c = self.new(exec_rule)
25
+ c.extend(Runners::Default)
26
+ c
27
+ end
28
+ end
29
+ end
30
+
31
+ module Runners
32
+
33
+ module Default
34
+ def run file
35
+ result = Open3.popen3(@exec_rule.gsub('%filename%', file)) do |stdin, stdout, stderr, wait_thr|
36
+ stderr.read.split("\n")
37
+ end
38
+ result
39
+ end
40
+ end
41
+
42
+ module Javascript
43
+ def run(root, files = [])
44
+ puts ''
45
+ JSLint::Lint.new(
46
+ :paths => files,
47
+ :config_path => 'config/jslint.yml'
48
+ ).run
49
+ []
50
+ rescue JSLint::LintCheckFailure
51
+ $stdmyout.puts "jslint checking failed"
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,116 @@
1
+ module Syntaxer
2
+ class Wizzard
3
+ # Interactive console wizzard
4
+ LANG = [:ruby, :haml, :sass, :javascript]
5
+ FOLDERS_RAILS = {
6
+ :ruby => ["app/*", "app/**/*", "lib/*", "lib/**/*"],
7
+ :haml => ["app/views/*", "app/views/**/*"],
8
+ :sass => ["public/stylesheets/sass/*", "public/stylesheets/sass/**/*"],
9
+ :javascript => ["public/javascript/*", "public/javascript/**/*"]
10
+ }
11
+ DEFAULT_GLOB = ["*/*", "**/*"]
12
+ FOLDERS = {
13
+ :ruby => DEFAULT_GLOB,
14
+ :haml => DEFAULT_GLOB,
15
+ :sass => DEFAULT_GLOB,
16
+ :javascript => DEFAULT_GLOB
17
+ }
18
+
19
+ attr_accessor :options
20
+
21
+ def initialize options
22
+ @options = options
23
+ end
24
+
25
+ def run
26
+ FileUtils.mkdir_p(@options[:config_path]) if !@options[:config_dir_exists] && @options[:create_config_dir]
27
+ config_full_path = File.join(@options[:config_path], Syntaxer::SYNTAXER_CONFIG_FILE_NAME)
28
+ File.open(config_full_path, 'w') do |f|
29
+ f.write(config)
30
+ end
31
+
32
+ if @options[:git]
33
+ options = {:root_path => @options[:root_path], :repository => :git, :config_file => File.join(@options[:config_path], Syntaxer::SYNTAXER_CONFIG_FILE_NAME)}
34
+ options.merge!({:rails => true}) if options[:rails]
35
+ Syntaxer.make_hook(options)
36
+ end
37
+
38
+ if @options[:rails] && @options[:languages].include?(:javascript)
39
+ system('rake jslint:copy_config')
40
+ end
41
+ end
42
+
43
+ def config
44
+ reader = Reader::DSLReader.build
45
+ writer = Writer.new(@options[:languages], reader.rules)
46
+ writer.get_config
47
+ end
48
+
49
+ class << self
50
+
51
+ def start
52
+ options = {}
53
+ options[:root_path] = Dir.getwd
54
+ rails = false
55
+ say("Some greeting message. Hello God of this computer! Please answer in couple of question:".color(:green))
56
+
57
+ # ask for the languages to install
58
+ to_install = []
59
+ LANG.each do |lang|
60
+ if agree("Install support of #{lang.to_s.capitalize}? (y/n)".color(:yellow))
61
+ default_paths = rails? ? FOLDERS_RAILS[lang] : FOLDERS[lang]
62
+ paths = ask("Paths to check separated by comma (default are #{default_paths.inspect}):".color(:yellow))
63
+ if paths.empty?
64
+ paths = default_paths
65
+ else
66
+ paths = paths.split(',').map{|l| l.gsub(/\s/,'')}
67
+ end
68
+ to_install.push([lang, paths])
69
+ end
70
+ end
71
+
72
+ options[:languages] = to_install
73
+
74
+ # ask for path to save config
75
+ if rails?
76
+ say("Rails application detected. Config file saved to config/synaxer.rb".color(:green))
77
+ rails = true
78
+ config_path = "config"
79
+ else
80
+ config_path = ask("Specify where to save syntaxer's config file (./ by default):".color(:yellow))
81
+ config_path = '.' if config_path.empty?
82
+ expanded_config_path = File.expand_path(config_path)
83
+ options[:config_dir_exists] = FileTest.directory?(expanded_config_path)
84
+ options[:create_config_dir] = agree("No such directory found #{expanded_config_path}. Create it?".color(:green)) unless options[:config_dir_not_exists]
85
+ end
86
+
87
+ options[:rails] = rails
88
+ options[:config_path] = config_path
89
+
90
+ # trying to detect GIT
91
+ begin
92
+ g = ::Git.open(options[:root_path])
93
+ options[:git] = agree("Found git repo in #{File.expand_path(options[:root_path])}. Would you like install hook to check syntax before every commit? (y/n)".color(:yellow))
94
+ rescue
95
+ options[:git] = false
96
+ end
97
+
98
+ Wizzard.new(options).run
99
+
100
+ # buy message
101
+ say("Syntaxer is configured and installed. You can edit config in #{File.join(config_path, Syntaxer::SYNTAXER_CONFIG_FILE_NAME)}".color(:green))
102
+
103
+ rescue Interrupt => e
104
+ # external quit
105
+ puts "\nBuy"
106
+ end
107
+
108
+ private
109
+
110
+ def rails?
111
+ FileTest.directory?("config") && FileTest.file?("config/application.rb") && FileTest.file?("config/routes.rb")
112
+ end
113
+
114
+ end
115
+ end
116
+ end