code-cleaner 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 – 2010 Jakub Šťastný aka Botanicus
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,64 @@
1
+ h1. About
2
+
3
+ Remove trailing whitespace, append missing \n and replace tabs by two spaces.
4
+
5
+ h1. Usage
6
+
7
+ <pre>
8
+ # edit script.rb and all Ruby scripts in lib directory
9
+ code-cleaner script.rb lib
10
+
11
+ # normalize standard input and print output to standard output
12
+ cat script.rb | code-cleaner
13
+
14
+ # add encoding declaration if missing
15
+ code-cleaner --encoding=utf-8
16
+ </pre>
17
+
18
+ h2. Blacklisting & Whitelisting
19
+
20
+ Of course you don't want to remove spaces from other files like PDF documents, images etc. You don't even want to remove spaces from all Ruby scripts, you probably want to ignore vendored files and locally installed gems. And this is where blacklisting and whitelisting comes handy.
21
+
22
+ Default behaviour is to ignore everything from vendor and gems directories and from other directories just files ending with .rb, .rake, .task and .thor are normalized.
23
+
24
+ You can change this behaviour by setting environment variables @BLACKLIST@ and @WHITELIST@. What you pass to these variables will be treated as a regular expression. The default behaviour respond to @BLACKLIST='/(vendor|gems)/'@ and @WHITELIST='\.(rb|rake|task|thor)$'@. Here's an example how you can use it:
25
+
26
+ <pre>
27
+ WHITELIST=''\.(rb|rake|nake|thor|task)$'' BLACKLIST='/(vendor|gems)/' code-cleaner .
28
+ </pre>
29
+
30
+ Note that these rules aren't applied for explicit arguments, so if you run @code-cleaner README.textile@, your readme actually will be normalized. It's because blacklisting and whitelisting rules are applied just on directories, so if you run @code-cleaner .@, your readme will stay untouched. Because of this reason @code-cleaner .@ and @code-cleaner *@ will be different.
31
+
32
+ If you want to force blacklisting resp. whitelisting even for explicitly specified files, use @--apply-rules@ switch. If you want just try it, use @--try-apply-rules@ which will tell you which files will be skipped and exit without any editing. If some files left in ARGV, the exit status will be 0, otherwise it will be 1.
33
+
34
+ In your Ruby projects, you might add all files in your @bin@ and @script@ directories into whitelist: @WHITELIST='(bin/[^/]+|.+\.(rb|rake|nake|thor|task))$'@
35
+
36
+ h2. Exit statuses
37
+
38
+ If code-cleaner does any changes, the exit status will be 0, otherwise it will be 10. It means you can use it in commands like:
39
+
40
+ <pre>
41
+ if code-cleaner "$file" --apply-rules; then # code-cleaner exits with 0 if some changes were made
42
+ git add "$file" # so the changes will be committed immediately
43
+ fi
44
+ </pre>
45
+
46
+ Or, better and more safe way is to directly check exit status of last command via @$?@ as is used in the default @pre-commit@ hook:
47
+
48
+ <pre>
49
+ code-cleaner .
50
+
51
+ if [ $? -eq 10 ]; then
52
+ echo Any modifications were made
53
+ else
54
+ echo File was changed
55
+ fi
56
+ </pre>
57
+
58
+ h2. Pre-commit hook
59
+
60
+ The best way how you can ensure your sources are clean is to use pre-commit hook, if your SCM provides this function. Plus, if you are using git, just use @load "code-cleaner.rake"@ in your Rakefile and then run @rake hooks:whitespace:install@. There are also alternatives for "Nake":http://github.com/botanicus/nake (@code-cleaner.nake@) and "Thor":http://github.com/wycats/thor (@code-cleaner.thor@). Second way is to use @rake --rakefile /path/to/code-cleaner/tasks/code-cleaner.rake hooks:whitespace:install@ resp. @nake /path/to/code-cleaner/tasks/code-cleaner.nake hooks:whitespace:install@ directly.
61
+
62
+ It might force you to remove your @.git/hooks/pre-commit@ if this file exist. If you haven't done any editation in this file, it's safe to do so and if you done some, then you will need to merge your changes manually.
63
+
64
+ If you project run in bundled environment, make sure you have your @bin@ or @script@ directory in your @$PATH@ or change the pre-commit hook to point to the right path to the @code-cleaner@.
data/bin/code-cleaner ADDED
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env ruby -i
2
+ # encoding: utf-8
3
+
4
+ # This is black magic!
5
+ # If you like Perl, you will probably enjoy it
6
+ # However it's easy to write and it works, so why not.
7
+
8
+ # Also be warned this script is evil – it might eat your
9
+ # cat or burn you house and I'm not responsible for it.
10
+
11
+ # == Normalize your source code == #
12
+ # 1) replace tabs by two space
13
+ # 2) remove trailing whitespace
14
+ # 3) add \n at the end of file unless it's already there
15
+
16
+ # == Usage == #
17
+ # code-cleaner app/controller/application_controller.rb
18
+ # cat app/controller/application_controller.rb | code-cleaner
19
+ #
20
+ # == Implementation == #
21
+ # The point is that ruby with -i will edit files
22
+
23
+ require "find"
24
+
25
+ $-i = "" # Windows compatibility
26
+
27
+ WHITELIST = Regexp.new(ENV["WHITELIST"] || '\.(rb|rake|nake|thor|task)$')
28
+ BLACKLIST = Regexp.new(ENV["BLACKLIST"] || '/(vendor|gems)/')
29
+
30
+ def permitted?(file)
31
+ WHITELIST.match(file) && ! BLACKLIST.match(file)
32
+ end
33
+
34
+ if ARGF.nil?
35
+ abort "Hey mate, what about some arguments or piped data? How the fuck you think I work?!"
36
+ end
37
+
38
+ # help
39
+ if ARGV.include?("-h") or ARGV.include?("--help")
40
+ abort "[\e[32mUsage\e[0m]\n"\
41
+ "code-cleaner script.rb # normalize script.rb\n"\
42
+ "cat script.rb | code-cleaner # normalize standard input and print output to standard output\n"\
43
+ "\n"\
44
+ "[\e[32mOptions\e[0m]\n"\
45
+ "--encoding=utf-8 # add encoding declaration if missing\n"\
46
+ "--apply-rules # apply blacklisting and whitelisting even on explicitly specified files\n"\
47
+ "--try-apply-rules # apply rules and exit successfuly if some files left in ARGV, fail otherwise\n"\
48
+ "\n"\
49
+ "[\e[32mEnvironment Variables\e[0m]\n"\
50
+ "BLACKLIST # patterns which will be ignored, defaults to /(vendor|gems)/\n"\
51
+ "WHITELIST # patterns which won't be ignored, defaults to \.(rb|rake|nake|thor|task)$\n"\
52
+ "\n"\
53
+ "[\e[32mTricks\e[0m]\n"\
54
+ "If you want to add files from your bin or script directory into \n"\
55
+ "whitelist, use WHITELIST='(bin/[^/]+|.+\.(rb|rake|nake|thor|task))$'"
56
+ "\n"\
57
+ "[\e[32mExamples\e[0m]\n"\
58
+ "WHITELIST='(bin/[^/]+|.+\.(rb|rake|nake|thor|task))$'"
59
+ end
60
+
61
+ def apply_rules
62
+ original = ARGV.dup
63
+ ARGV.delete_if { |file| File.file?(file) && ! permitted?(file) }
64
+ puts "~ Skipping #{original - ARGV}" unless ARGV == original
65
+ end
66
+
67
+ if ARGV.delete("--try-apply-rules")
68
+ apply_rules
69
+ ARGV.empty? ? exit(1) : exit
70
+ end
71
+
72
+ if ARGV.delete("--apply-rules")
73
+ apply_rules
74
+ end
75
+
76
+ unless ARGV.grep(/^--encoding=(.+)$/).empty?
77
+ argument = ARGV.grep(/^--encoding=(.+)$/).first
78
+ ARGV.delete(argument)
79
+ argument.match(/^--encoding=(.+)$/)
80
+ encoding = $1.dup
81
+ else
82
+ encoding = nil
83
+ end
84
+
85
+ # expand all directories to list of files
86
+ if ARGV.any? { |file| File.directory?(file) }
87
+ args = Array.new
88
+ ARGV.each do |item|
89
+ if File.directory?(item)
90
+ Find.find(item) do |file|
91
+ if File.file?(file) && permitted?(file)
92
+ args.push(file)
93
+ end
94
+ end
95
+ else
96
+ args.push(item)
97
+ end
98
+ end
99
+ ARGV.clear.push(*args)
100
+ puts "~ Expanding args into #{ARGV.inspect}"
101
+ end
102
+
103
+ # Ruby 1.9 will warn that you can't do in place edit for stdio,
104
+ # so we will disable in place mode. Yes, in Ruby 1.9 you can finally
105
+ # turn in place mode on or off from API in better way than just $-i = false.
106
+ if ARGF.respond_to?(:inplace_mode) && ARGV.empty?
107
+ ARGF.inplace_mode = false
108
+ end
109
+
110
+ # default exit status is 10 which signalize that we actually haven't changed anything
111
+ status = 10
112
+
113
+ # This is the only way how I was able to inspect if STDIN is empty. Magic, right?
114
+ # NOTE: if this shows as a bad idea, another way might be to put "/dev/null" resp. "NUL" on Windows to the ARGV
115
+ begin
116
+ STDIN.read_nonblock(1)
117
+ rescue Errno::EAGAIN
118
+ # The point of inspecting if STDIN and ARGV are empty is that we might
119
+ # have an empty ARGF if anything passed and if so, it will wait for an interactive input which we don't want
120
+ unless ARGV.empty?
121
+ while line = ARGF.gets
122
+ # I was trying to use ARGF.lineno.eql?(1), but it doesn't work. First problem
123
+ # was it didn't work with ARGF#each_line because IO#lineno count how many times
124
+ # IO#gets was called rather than how many \n the stream have. It isn't any
125
+ # serious problem, however when we have multiple files in ARGV, then the
126
+ # lineno isn't cleaned after the file in ARGF is switched.
127
+ # The trick with line.length == ARGF.pos is simple, IO#pos returns position
128
+ # of last character which was readed by gets or similar method, so if
129
+ # the position is same as is the line, then it has to be the first line.
130
+ if line.length == ARGF.pos && encoding && ! line.match(/^#.*coding/)
131
+ puts "# encoding: #{encoding}"
132
+ end
133
+ # You might be wondering WTF is going on, I'm editing files, so I have to use File.open, do the modification on the original content and then save it. The point is we used -i switch which is good for in place editing
134
+ # flag that we actually changed the file
135
+ status = 0 if line.match(/^\s*\t\s*/) || ! line.match(/\n/)
136
+ puts line.gsub(/\t/, " ").rstrip
137
+ end
138
+ end
139
+ end
140
+
141
+ exit status
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env gem build
2
+ # encoding: utf-8
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "code-cleaner"
6
+ s.version = "0.1"
7
+ s.authors = ["Jakub Šťastný aka Botanicus"]
8
+ s.homepage = "http://github.com/botanicus/code-cleaner"
9
+ s.summary = "Remove trailing whitespace, append missing \\n and replace tabs by two spaces"
10
+ s.description = "" # TODO: long description
11
+ s.cert_chain = nil
12
+ s.email = ["knava.bestvinensis", "gmail.com"].join("@")
13
+ s.has_rdoc = false
14
+
15
+ # so you can use load "code-cleaner.rake" in you Rakefile
16
+ s.require_paths = ["tasks"]
17
+
18
+ # files
19
+ s.files = Dir["**/*"]
20
+ s.executables = ["code-cleaner"]
21
+ s.default_executable = "code-cleaner"
22
+
23
+ # RubyForge
24
+ s.rubyforge_project = "code-cleaner"
25
+ end
@@ -0,0 +1,37 @@
1
+ #!/bin/sh
2
+
3
+ echo "Entering pre-commit hook ..."
4
+
5
+ # If you haven't done any commit yet,
6
+ # git diff will fail with following message:
7
+ # fatal: No HEAD commit to compare with (yet)
8
+
9
+ # TODO: how to get list of files which will be
10
+ # committed if there isn't any commit in the index yet?
11
+ # For now we just use normalize everything in current
12
+ # directory, but it isn't very good solution.
13
+ files=$(git diff --staged --name-only 2> /dev/null || echo ".")
14
+
15
+ # NOTE: we are using printf rather than echo because printf should be
16
+ # more portable, each echo implementation has quite different behaviour
17
+ if which code-cleaner &> /dev/null; then
18
+ for file in $files; do
19
+ if code-cleaner "$file" --try-apply-rules &> /dev/null ; then
20
+ printf "Normalizing \"\e[36m$file\e[0m\" "
21
+ code-cleaner "$file" --apply-rules &> /dev/null
22
+ # NOTE: we have to check the exit status directly,
23
+ # so we can be sure that the command just wasn't found
24
+ if [ $? -eq 10 ]; then # code-cleaner exits with 0 if some changes were made
25
+ printf "[\e[32mCLEAN\e[0m]\n"
26
+ else
27
+ printf "[\e[33mDONE\e[0m]\n"
28
+ git add "$file" # so the changes will be committed immediately
29
+ fi
30
+ else
31
+ printf "Skipping \"\e[36m$file\e[0m\"\n"
32
+ fi
33
+ done
34
+ else
35
+ printf "[\e[31mERROR\e[0m] You have to install code-cleaner first!\n"
36
+ exit 1
37
+ fi
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require "fileutils"
4
+ require "nake/task"
5
+
6
+ Nake::Task.new("hooks:whitespace:install") do |task|
7
+ task.description = "Install hook for automatically removing trailing whitespace"
8
+ task.define do
9
+ if File.exist?(".git/hooks/pre-commit")
10
+ abort "You must remove .git/hooks/pre-commit first!"
11
+ else
12
+ begin
13
+ puts "Installing .git/hooks/pre-commit ..."
14
+ source = File.join(File.dirname(__FILE__), "..", "support", "pre-commit")
15
+ FileUtils.install source, ".git/hooks/pre-commit", :mode => 0755
16
+ rescue Errno::ENOENT
17
+ abort "You have to run git init first!"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ desc "Install hook for automatically removing trailing whitespace"
4
+ task "hooks:whitespace:install" do
5
+ if File.exist?(".git/hooks/pre-commit")
6
+ abort "You must remove .git/hooks/pre-commit first!"
7
+ else
8
+ begin
9
+ puts "Installing .git/hooks/pre-commit ..."
10
+ source = File.join(File.dirname(__FILE__), "..", "support", "pre-commit")
11
+ install source, ".git/hooks/pre-commit", :mode => 0755
12
+ rescue Errno::ENOENT
13
+ abort "You have to run git init first!"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ require "fileutils"
4
+
5
+ module Hooks
6
+ class Whitespace < Thor
7
+ desc "install", "Install hook for automatically removing trailing whitespace"
8
+ def install
9
+ if File.exist?(".git/hooks/pre-commit")
10
+ abort "You must remove .git/hooks/pre-commit first!"
11
+ else
12
+ puts "Installing .git/hooks/pre-commit ..."
13
+ source = File.join(File.dirname(__FILE__), "..", "support", "pre-commit")
14
+ FileUtils.install source, ".git/hooks/pre-commit", :mode => 0755
15
+ end
16
+ rescue Errno::ENOENT
17
+ abort "You have to run git init first!"
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: code-cleaner
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - "Jakub \xC5\xA0\xC5\xA5astn\xC3\xBD aka Botanicus"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ date: 2009-12-28 00:00:00 +01:00
12
+ default_executable: code-cleaner
13
+ dependencies: []
14
+
15
+ description: ""
16
+ email: knava.bestvinensis@gmail.com
17
+ executables:
18
+ - code-cleaner
19
+ extensions: []
20
+
21
+ extra_rdoc_files: []
22
+
23
+ files:
24
+ - bin/code-cleaner
25
+ - code-cleaner-0.1.gem
26
+ - code-cleaner.gemspec
27
+ - LICENSE
28
+ - README.textile
29
+ - support/pre-commit
30
+ - tasks/code-cleaner.nake
31
+ - tasks/code-cleaner.rake
32
+ - tasks/code-cleaner.thor
33
+ has_rdoc: true
34
+ homepage: http://github.com/botanicus/code-cleaner
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - tasks
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project: code-cleaner
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Remove trailing whitespace, append missing \n and replace tabs by two spaces
61
+ test_files: []
62
+