header-inserter 1.0.1

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.
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/../../lib/header-inserter/project'
2
+ require File.dirname(__FILE__) + '/../../lib/header-inserter/project_file'
3
+ require 'ftools'
4
+
5
+ non_existing = "/tmp/non_existing_project" + rand(1000000).to_s
6
+
7
+ empty = "/tmp/empty_project" + rand(1000000).to_s
8
+ Dir.mkdir empty
9
+
10
+ non_structured = "/tmp/non_structured_project" + rand(1000000).to_s
11
+ Dir.mkdir non_structured
12
+ main = File.new non_structured+"/Main.java", "w"
13
+ main.puts "class Main {}"
14
+ main.close
15
+ mainTest = File.new non_structured+"/MainTest.java", "w"
16
+ mainTest.puts "class MainTest {}"
17
+ mainTest.close
18
+ mainTestClass = File.new non_structured+"/MainTest.class", "w"
19
+ array = (1..500).to_a.map{|x| rand(256)}
20
+ mainTestClass.puts array.pack("c*")
21
+ mainTestClass.close
22
+
23
+ structured = "/tmp/structured_project" + rand(1000000).to_s
24
+ File.makedirs structured+"/src/my/project/internal"
25
+ File.makedirs structured+"/bin/my/project/internal"
26
+ File.makedirs structured+"/test/my/project/internal"
27
+ main = File.new structured+"/src/my/project/Main.java", "w"
28
+ main.puts "package my.project; class Main {}"
29
+ main.close
30
+ logic = File.new structured+"/src/my/project/internal/Logic.java", "w"
31
+ logic.puts "package my.project.internal; class Logic {}"
32
+ logic.close
33
+ logicClass = File.new structured+"/bin/my/project/internal/Logic.class", "w"
34
+ array = (1..500).to_a.map{|x| rand(256)}
35
+ logicClass.puts array.pack("c*")
36
+ logicClass.close
37
+ logicTest = File.new structured+"/test/my/project/internal/LogicTest.java", "w"
38
+ logicTest.puts "package my.project.internal; class LogicTest {}"
39
+ logicTest.close
40
+
41
+ Given /a[n]? (\S*) project/ do |type|
42
+ @project = Project.new eval(type.gsub("-","_"))
43
+ end
44
+
45
+ When /I list "(.*)" files/ do |type|
46
+ @files = @project.list(type.to_sym)
47
+ end
48
+
49
+ Then /I should get nothing/ do
50
+ @files.should == []
51
+ end
52
+
53
+ Then /I should get (\[".*"(?:, ".*")?\])/ do |list|
54
+ @files.sort.should == eval(list).map{|path| ProjectFile.new @project, path }.sort
55
+ end
56
+
57
+ at_exit do
58
+ FileUtils.rm_rf empty
59
+ FileUtils.rm_rf non_structured
60
+ FileUtils.rm_rf structured
61
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + "/../../lib/header-inserter"
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module HeaderInserter
5
+ VERSION = '1.0.1'
6
+ end
@@ -0,0 +1,31 @@
1
+ require 'date'
2
+
3
+ class Modification
4
+ include Comparable
5
+
6
+ @@revision_format = /r(\d{1,}) \| (\S+) \| (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ([+|-]?\d{2})(\d{2}) .* \| \d{1,} line[s]?\n\n(.*)\n/m
7
+ @@svn_time_format = "%Y-%m-%d %H:%M:%S %z"
8
+ attr_reader :revision, :author, :date, :log
9
+
10
+ def Modification.parse text_log
11
+ if @@revision_format.match text_log
12
+ revision = $1.to_i
13
+ author = $2
14
+ time_zone = ($4 + ":" + $5)
15
+ date = DateTime.strptime($3 + " " + time_zone, @@svn_time_format)
16
+ log = $6
17
+ Modification.new revision, author, date, log
18
+ end
19
+ end
20
+
21
+ def initialize revision, author, date, log
22
+ @revision = revision
23
+ @author = author
24
+ @date = date
25
+ @log = log
26
+ end
27
+
28
+ def <=> other
29
+ revision <=> other.revision
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ class NilVersionControl
2
+ @@instance = self.new
3
+
4
+ def NilVersionControl.new
5
+ @@instance
6
+ end
7
+
8
+ def history file_path
9
+ []
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ require 'find'
2
+ require File.dirname(__FILE__) + '/project_file'
3
+ require File.dirname(__FILE__) + '/nil_version_control'
4
+ require File.dirname(__FILE__) + '/svn_version_control'
5
+
6
+ class Project
7
+ include Comparable
8
+ attr_reader :path
9
+
10
+ def initialize path
11
+ @path = File.expand_path(path)
12
+ end
13
+
14
+ def <=> other
15
+ @path <=> other.path
16
+ end
17
+
18
+ def list file_pattern
19
+ if file_pattern.is_a? Symbol
20
+ file_pattern = /.*\.#{file_pattern.to_s}$/
21
+ end
22
+ files = []
23
+ Find.find @path do |entry|
24
+ short_entry = entry.sub /^#{@path+File::SEPARATOR}/, ""
25
+ if file_pattern.match short_entry
26
+ version_control = obtain_version_control short_entry
27
+ files << ProjectFile.new(self, short_entry, version_control)
28
+ end
29
+ end
30
+ files
31
+ end
32
+
33
+ protected
34
+
35
+ def obtain_version_control path
36
+ entries_path = @path + "/#{File.dirname(path)}/.svn/entries"
37
+ file_name = File.basename(path)
38
+ entries = File.new entries_path, "r"
39
+ contents = []
40
+ entries.each{|line| contents << line}
41
+ svn_path = contents[4].strip.gsub(/#{File.dirname(path)}/, "")
42
+ entry_for_file = contents.collect{|line| /^#{file_name}$/.match line }
43
+ return SvnVersionControl.new(svn_path) unless entry_for_file.empty?
44
+ rescue Errno::ENOENT
45
+ NilVersionControl.new
46
+ end
47
+ end
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) + '/project'
2
+ require File.dirname(__FILE__) + '/nil_version_control'
3
+ require File.dirname(__FILE__) + '/svn_version_control'
4
+ require 'date'
5
+ require 'etc'
6
+
7
+ class ProjectFile
8
+ include Comparable
9
+ attr_reader :project, :path, :version_control
10
+
11
+ def initialize project, path, version_control = NilVersionControl.new
12
+ if project.nil?
13
+ @project = Project.new "/"
14
+ else
15
+ @project = project
16
+ end
17
+ @path = path
18
+ @version_control = version_control
19
+ end
20
+
21
+ def <=> other
22
+ absolute_path <=> other.absolute_path
23
+ end
24
+
25
+ def absolute_path
26
+ absolute = @project.path
27
+ absolute += File::SEPARATOR if @project.path != "/"
28
+ absolute + @path
29
+ end
30
+
31
+ def original_header
32
+ file = File.new absolute_path, "r"
33
+ header = ""
34
+ while (line = file.readline) and not(/\s*(?:package|class|import)\s*/.match line)
35
+ header += line
36
+ end
37
+ header
38
+ rescue Errno::ENOENT
39
+ return nil
40
+ end
41
+
42
+ def created_on
43
+ return Date.today if modifications.empty?
44
+ modifications[0].date
45
+ end
46
+
47
+ def contributors
48
+ return [username.strip] if modifications.empty?
49
+ modifications.map{ |mod| mod.author.strip }.uniq
50
+ end
51
+
52
+ def generate_header header, hooks
53
+ generated_header = header
54
+ hooks.keys.each do |key|
55
+ value = hooks[key].call(self)
56
+ if not value.is_a?(String)
57
+ puts "Error while evaluating the hook for #{key}. Returned a #{value.class} valued as '#{value}'. Calling to_s."
58
+ end
59
+ generated_header = generated_header.gsub key.to_s, value.to_s
60
+ end
61
+ generated_header
62
+ end
63
+
64
+ def add_header header, old_header = nil
65
+ content = header
66
+ file = File.new absolute_path, "r"
67
+ file.each do |line|
68
+ content += line
69
+ end
70
+ file.close
71
+
72
+ content.gsub(old_header, "") unless old_header.nil?
73
+
74
+ file = File.new absolute_path, "w"
75
+ file.puts content
76
+ file.close
77
+ end
78
+
79
+ protected
80
+
81
+ def modifications
82
+ @mods = version_control.history path if @mods.nil?
83
+ @mods
84
+ end
85
+
86
+ def username
87
+ Etc.getlogin
88
+ end
89
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/modification'
2
+
3
+ class SvnVersionControl
4
+ attr_reader :path
5
+ @@entry_start = /^[-]{72}$/
6
+ @@valid = /\s*r\d{1,} \| \S+ \| .* | \d{1,} line[s]?\n\n.*\n/
7
+
8
+ def initialize server_path
9
+ @path = server_path
10
+ @logs = {}
11
+ end
12
+
13
+ def retrieve_log file_path
14
+ if not @logs.has_key?(file_path)
15
+ @logs[file_path] = %x[svn log #{@path + file_path}]
16
+ end
17
+ @logs[file_path]
18
+ end
19
+
20
+ def history file_path
21
+ mods = []
22
+ log = retrieve_log file_path
23
+ log.split(@@entry_start).each do |entry|
24
+ mods << Modification.parse(entry) if @@valid.match(entry)
25
+ end
26
+ mods.reverse
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../lib/header-inserter/modification'
2
+ require 'date'
3
+
4
+ describe Modification do
5
+ it "should be created with revision number, author, date and log entry" do
6
+ date = Date.today
7
+ mod = Modification.new 1, "Hugo", date, "Just a test"
8
+ mod.author.should == "Hugo"
9
+ mod.revision.should == 1
10
+ mod.date.should == date
11
+ mod.log.should == "Just a test"
12
+ end
13
+
14
+ it "should parse a complete log entry" do
15
+ date = DateTime.strptime "2009-03-17 17:21:03 -0300", "%Y-%m-%d %H:%M:%S %z"
16
+ mod = Modification.parse """r1451 | hugo.corbucci | 2009-03-17 17:21:03 -0300 (Ter, 17 Mar 2009) | 2 lines
17
+
18
+ Adding the projects for erase and tests for it. I forgot it before.
19
+ Hugo
20
+ """
21
+ mod.author.should == "hugo.corbucci"
22
+ mod.revision.should == 1451
23
+ mod.date.should == date
24
+ mod.log.should == "Adding the projects for erase and tests for it. I forgot it before.\nHugo"
25
+ end
26
+
27
+ it "should sort according to the revision" do
28
+ date = Date.today - 1
29
+ first_mod = Modification.new 1, "Hugo", date, "Just a test"
30
+ second_mod = Modification.new 2, "Hugo", Date.today, "Just a test"
31
+ equal_mod = Modification.new 1, "Hugo", date, "Just a test"
32
+ (first_mod<=>second_mod).should == -1
33
+ (second_mod<=>first_mod).should == 1
34
+ (first_mod<=>first_mod).should == 0
35
+ (first_mod<=>equal_mod).should == 0
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../lib/header-inserter/nil_version_control'
2
+
3
+ describe NilVersionControl do
4
+ it "should have only one instance" do
5
+ instance1 = NilVersionControl.new
6
+ instance2 = NilVersionControl.new
7
+ instance1.should == instance2
8
+ end
9
+
10
+ it "should always return an empty history" do
11
+ vc = NilVersionControl.new
12
+ vc.history("a").should == []
13
+ vc.history("http://svn.archimedes.org.br/public/mainarchimedes/rcparchimedes/br.org.archimedes.izpack/ArchimedesProjectSet.psf").should == []
14
+ end
15
+ end
@@ -0,0 +1,202 @@
1
+ require File.dirname(__FILE__) + '/../lib/header-inserter/project'
2
+ require File.dirname(__FILE__) + '/../lib/header-inserter/svn_version_control'
3
+ require File.dirname(__FILE__) + '/../lib/header-inserter/modification'
4
+ require 'ftools'
5
+ require 'fileutils'
6
+
7
+ describe ProjectFile do
8
+ before(:each) do
9
+ @project = Project.new "/tmp/header-insert-test-project"
10
+ end
11
+
12
+ it "should be created with a project, a path relative to that project (assuming NilVersionControl)" do
13
+ file = ProjectFile.new @project, "non-existing-file.rb"
14
+ file.absolute_path.should == "/tmp/header-insert-test-project/non-existing-file.rb"
15
+ file.version_control.should be_kind_of(NilVersionControl)
16
+ end
17
+
18
+ it "should be created with a project, a path relative to that project and a version control system" do
19
+ file = ProjectFile.new @project, "non-existing-file.rb", SvnVersionControl.new("http://svn.archimedes.org.br/public/")
20
+ file.absolute_path.should == "/tmp/header-insert-test-project/non-existing-file.rb"
21
+ file.version_control.should be_kind_of(SvnVersionControl)
22
+ end
23
+
24
+ it "should be assume '/' as a root path if the project is nil" do
25
+ file = ProjectFile.new nil, "testfile.rb"
26
+ file.absolute_path.should == "/testfile.rb"
27
+ end
28
+
29
+ it "should compare based on the relative path" do
30
+ file = ProjectFile.new @project, "a.file"
31
+ other_file = ProjectFile.new @project, "z.file"
32
+ (file<=>other_file).should == -1
33
+ (other_file<=>file).should == 1
34
+ (file<=>file).should == 0
35
+ (other_file<=>other_file).should == 0
36
+ end
37
+
38
+ it "should equal based on the absolute and the path" do
39
+ file = ProjectFile.new @project, "a.file"
40
+ other_file = ProjectFile.new @project, "z.file"
41
+ equal_file = ProjectFile.new @project, "a.file"
42
+
43
+ file.should == file
44
+ file.should_not == other_file
45
+ file.should == equal_file
46
+
47
+ other_file.should == other_file
48
+ other_file.should_not == file
49
+ other_file.should_not == equal_file
50
+ end
51
+
52
+ it "should retrieve an empty original header from a non existing file" do
53
+ file = ProjectFile.new @project, "a.file"
54
+ file.original_header.should be_nil
55
+ end
56
+
57
+ it "should retrieve all the header until package from an existing file" do
58
+ file = ProjectFile.new @project, "my/project/A.java"
59
+ header = "/**\n * A simple test\n */\n\n"
60
+
61
+ create_file file.absolute_path, "#{header}package my.project;\n\nclass A {}"
62
+ file.original_header.should == header
63
+ end
64
+
65
+ it "should retrieve all the header until import from an existing file without package" do
66
+ file = ProjectFile.new @project, "A.java"
67
+ header = "/**\n * A simple test\n */\n\n"
68
+
69
+ create_file file.absolute_path, "#{header}import java.util.Observable;\n\nclass A extends Observable {}"
70
+ file.original_header.should == header
71
+ end
72
+
73
+ it "should retrieve all the header until class from an existing file without package or imports" do
74
+ file = ProjectFile.new @project, "A.java"
75
+ header = "/**\n * A simple test\n */\n\n"
76
+
77
+ create_file file.absolute_path, "#{header}class A {}"
78
+ file.original_header.should == header
79
+ end
80
+
81
+ it "should retrieve created on information from modifications from version control" do
82
+ mock_version_control = mock(NilVersionControl.new)
83
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([])
84
+ file = ProjectFile.new @project, "A.java", mock_version_control
85
+ file.created_on.should == Date.today
86
+
87
+ date = DateTime.parse("2009-01-01T17:32:45-03:00")
88
+ mock_version_control = mock(NilVersionControl.new)
89
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([Modification.new(134, "hugo", date, "Unique entry")])
90
+ file = ProjectFile.new @project, "A.java", mock_version_control
91
+ file.created_on.should == date
92
+
93
+ mock_version_control = mock(NilVersionControl.new)
94
+ date1 = DateTime.parse("2009-01-01T17:32:45-03:00")
95
+ date2 = DateTime.parse("2009-02-12T09:02:56+08:00")
96
+ date3 = DateTime.parse("2009-03-24T19:45:30+00:00")
97
+ mods = [Modification.new(134, "hugo", date1, "First entry"),
98
+ Modification.new(531, "mari", date2, "Second entry"),
99
+ Modification.new(1039, "hugo", date3, "Third and last entry")]
100
+ mock_version_control.should_receive(:history).with("A.java").once.and_return(mods)
101
+ file = ProjectFile.new @project, "A.java", mock_version_control
102
+ file.created_on.should == date1
103
+ end
104
+
105
+ it "should retrieve contributors from modifications from version control with unique names sorted by first occurrence" do
106
+ mock_version_control = mock(NilVersionControl.new)
107
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([])
108
+ file = ProjectFile.new @project, "A.java", mock_version_control
109
+ file.contributors.should == ["night"]
110
+
111
+ date = DateTime.parse("2009-01-01T17:32:45-03:00")
112
+ mock_version_control = mock(NilVersionControl.new)
113
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([Modification.new(134, "hugo", date, "Unique entry")])
114
+ file = ProjectFile.new @project, "A.java", mock_version_control
115
+ file.contributors.should == ["hugo"]
116
+
117
+ mock_version_control = mock(NilVersionControl.new)
118
+ date1 = DateTime.parse("2009-01-01T17:32:45-03:00")
119
+ date2 = DateTime.parse("2009-02-12T09:02:56+08:00")
120
+ date3 = DateTime.parse("2009-03-24T19:45:30+00:00")
121
+ date4 = DateTime.parse("2009-03-24T20:01:13-03:00")
122
+ date5 = DateTime.parse("2009-03-24T20:11:16-03:00")
123
+ mods = [Modification.new(134, "hugo", date1, "First entry"),
124
+ Modification.new(531, "mari", date2, "Second entry"),
125
+ Modification.new(1039, "hugo", date3, "Third entry"),
126
+ Modification.new(1045, "kung", date4, "Fourth entry"),
127
+ Modification.new(1253, "mari", date5, "Last entry")]
128
+ mock_version_control.should_receive(:history).with("A.java").once.and_return(mods)
129
+ file = ProjectFile.new @project, "A.java", mock_version_control
130
+ file.contributors.should == ["hugo", "mari", "kung"]
131
+ end
132
+
133
+ it "should have a nil version control by default" do
134
+ file = ProjectFile.new @project, "A.java"
135
+ header = "/**\n * A simple test\n */\n\n"
136
+
137
+ create_file file.absolute_path, "#{header}class A {}"
138
+ file.version_control.should == NilVersionControl.new
139
+ end
140
+
141
+ it "should retrieve a nil history if not under subversion control"
142
+ it "should retrieve a single modification history if recently under subversion control"
143
+ it "should retrieve multiple modifications history if under subversion control for a long time"
144
+
145
+ it "should generate a header according to a header format and hooks" do
146
+ header = "# My fine header.\n# It has the date (REPLACE_DATE) and the first contributor (FIRST_CONTRIBUTOR).\n # And ends like nothing\n"
147
+ hooks = { :REPLACE_DATE => lambda { |file| file.created_on.strftime "%Y-%m-%d, %H:%M:%S" },
148
+ :FIRST_CONTRIBUTOR => lambda {|file| file.contributors[0] } }
149
+
150
+ expected_header = "# My fine header.\n# It has the date (2009-03-24, 21:43:58) and the first contributor (hugo).\n # And ends like nothing\n"
151
+
152
+ date = DateTime.parse "2009-03-24T21:43:58-03:00"
153
+ mock_version_control = mock(NilVersionControl.new)
154
+ mock_version_control.should_receive(:history).with("A.java").once.and_return([Modification.new(324, "hugo", date, "Unique entry")])
155
+ file = ProjectFile.new @project, "A.java", mock_version_control
156
+ file.generate_header(header, hooks).should == expected_header
157
+ end
158
+
159
+ it "should add a header at the beginning of the file" do
160
+ new_header = "/**\n * My fine header.\n * It has the date (2009-03-24, 21:43:58) and the first contributor (hugo).\n * And ends like nothing\n */\n\n"
161
+ content = "class A {}\n"
162
+
163
+ file = ProjectFile.new @project, "A.java"
164
+ create_file file.absolute_path, content
165
+ file.add_header(new_header)
166
+
167
+ read_content(file.absolute_path).should == new_header + content
168
+ end
169
+
170
+ it "should add a header at the beginning of the file and remove the one specified" do
171
+ new_header = "/**\n * My fine header.\n * It has the date (2009-03-24, 21:43:58) and the first contributor (hugo).\n * And ends like nothing\n */\n\n"
172
+ stupid_header = "/** A stupid header */\n\n"
173
+ content = "#{stupid_header}class A {}\n"
174
+
175
+ file = ProjectFile.new @project, "A.java"
176
+ create_file file.absolute_path, content
177
+ file.add_header(new_header, stupid_header)
178
+
179
+ read_content(file.absolute_path).should == new_header + content
180
+ end
181
+
182
+ after(:each) do
183
+ FileUtils.rm_rf @project.path
184
+ end
185
+
186
+ protected
187
+
188
+
189
+ def create_file absolute_path, content
190
+ File.makedirs File.dirname(absolute_path)
191
+ file = File.new absolute_path, "w"
192
+ file.puts content
193
+ file.close
194
+ end
195
+
196
+ def read_content absolute_path
197
+ file = File.new absolute_path, "r"
198
+ content = file.inject { |result , line| result + line }
199
+ file.close
200
+ content
201
+ end
202
+ end