sitebuilder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
File without changes
data/COPYING ADDED
@@ -0,0 +1,32 @@
1
+ SiteBuilder Licence
2
+
3
+ COPYRIGHT AND PERMISSION NOTICE
4
+
5
+ Copyright (c) 2009 Green Bar Software Limited, UK
6
+
7
+ All rights reserved.
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a
10
+ copy of this software and associated documentation files (the
11
+ "Software"), to deal in the Software without restriction, including
12
+ without limitation the rights to use, copy, modify, merge, publish,
13
+ distribute, and/or sell copies of the Software, and to permit persons
14
+ to whom the Software is furnished to do so, provided that the above
15
+ copyright notice(s) and this permission notice appear in all copies of
16
+ the Software and that both the above copyright notice(s) and this
17
+ permission notice appear in supporting documentation.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
22
+ OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23
+ HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
24
+ INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
25
+ FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
26
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
27
+ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28
+
29
+ Except as contained in this notice, the name of a copyright holder
30
+ shall not be used in advertising or otherwise to promote the sale, use
31
+ or other dealings in this Software without prior written authorization
32
+ of the copyright holder.
data/README.rdoc ADDED
@@ -0,0 +1,28 @@
1
+ = SiteBuilder
2
+ SiteBuilder is a rule-based static website generator. It performs an inorder traversal of each
3
+ file and directory in a source directory, applying exactly one transformation action per file.
4
+
5
+ Each transformation action gets the opportunity to write output to a corresponding directory in
6
+ a subdirectory of a destination directory.
7
+
8
+ * The action to be used on each file or directory is determined by file extension.
9
+ * Actions should not depend on the order in which they run, or on the output of other actions.
10
+ * Actions may use any part of the source directory.
11
+ * No action should change the source directory or its contents.
12
+
13
+ = How to use it
14
+ * Just a core object model & tests right now... needs to be completed.
15
+
16
+ == RDOC API
17
+ The rdoc can be found at http://www.greenbarsoft.co.uk/software/sitebuilder/rdoc/
18
+
19
+ == Compatibility
20
+ This project is being developed on OS X. Automated testing for Linux will be included in future releases.
21
+
22
+ == Licence
23
+ This is open source software and comes with no warranty. See COPYING for details.
24
+
25
+
26
+ http://www.greenbarsoft.co.uk
27
+
28
+ Copyright 2009 Green Bar Software Limited, UK
data/TODO.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ == todo
2
+ * make template action
3
+ * merge a text formatter into template action
4
+ * make copy action
5
+ * make index action
6
+ * work out how to handle cross references
7
+ * work out how to handle non-fatal errors
8
+ * work out how to handle fatal errors
9
+ * integrate SemanticText action
@@ -0,0 +1,24 @@
1
+ class Array
2
+
3
+ # execute block for each element of array, passing the element as the block
4
+ # parameter until the block returns true
5
+ def each_until(&block)
6
+ return false if empty?
7
+ for i in 0..size
8
+ result_true = block.call(self[i])
9
+ break if result_true
10
+ end
11
+ if result_true
12
+ return true
13
+ else
14
+ return false
15
+ end
16
+ end
17
+
18
+ # return a subarray consisting of elements 2..n of the array
19
+ # (i.e. all except the first element)
20
+ def tail
21
+ return self[1,size-1] if size>1
22
+ return []
23
+ end
24
+ end
@@ -0,0 +1,95 @@
1
+ require 'FileUtils'
2
+ module SiteBuilder
3
+
4
+ # Abstract class which is the abstract base class for file system instances
5
+ class FsEntry
6
+ # This is the canonical path to the file system entry
7
+ attr_reader :path
8
+
9
+ def initialize(path)
10
+ @path = File.expand_path(path)
11
+ end
12
+
13
+ def ==(other)
14
+ self.path==(other.path)
15
+ end
16
+
17
+ def extname
18
+ File::extname(@path)
19
+ end
20
+
21
+ # the bare filename without the file extension
22
+ def extnless
23
+ b = basename
24
+ b[0,b.size-extname.size]
25
+ end
26
+
27
+ def basename
28
+ File::basename(@path)
29
+ end
30
+
31
+ def dirname
32
+ File::dirname(@path)
33
+ end
34
+
35
+ def ctime
36
+ File::ctime(@path)
37
+ end
38
+
39
+ def size
40
+ File.size(@path)
41
+ end
42
+
43
+ # creates a file system entry for a fully qualified pathname
44
+ def self.fs_entry_from(f)
45
+ return FileEntry.new(f) if File.file?(f)
46
+ return DirEntry.new(f) if File.directory?(f)
47
+ UnknownEntry.new(f)
48
+ end
49
+
50
+ # creates a file system entry for a filename in the current file system entry object
51
+ def fs_entry_from(fs_entry)
52
+ FsEntry.fs_entry_from(File.join(@path, fs_entry))
53
+ end
54
+
55
+ end
56
+
57
+ # I represent a file in the filesystem
58
+ class FileEntry < FsEntry
59
+
60
+ # I am visitable with a traverser.
61
+ def traverse(traverser)
62
+ traverser.traverse_file(self)
63
+ end
64
+ end
65
+
66
+ # I represent filesystem entries that have been detected but aren't supported.
67
+ class UnknownEntry < FsEntry
68
+ end
69
+
70
+ # I represent directories in the filesystem.
71
+ class DirEntry < FsEntry
72
+
73
+ # I am visitable with a Traverser. I perform inorder traversal of FsEntries.
74
+ def traverse(traverser)
75
+ traverser.traverse_dir(self)
76
+ Dir.new(@path).each do |stringpath|
77
+ subentry = fs_entry_from(stringpath)
78
+ subentry.traverse(traverser) unless stringpath=='.' || stringpath=='..'
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ # Visitor for traversing a filesystem
85
+ class Traverser
86
+ # callback when traversing a DirEntry object
87
+ def traverse_dir(dir_entry)
88
+ end
89
+
90
+ # callback when traversing a FileEntry object
91
+ def traverse_file(file_entry)
92
+ end
93
+ end
94
+
95
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/ruby
2
+ require 'sitebuilder/sitegenerator'
3
+ module SiteBuilder
4
+
5
+ swapout = File.join(ENV['SANDBOX'],'sitebuilder','examples','homepage','source')
6
+ replace = File.join(ENV['SANDBOX'],'sitebuilder','examples','homepage','dest')
7
+
8
+ s = SiteGenerator.new(swapout,replace)
9
+ s.add_action('.art') {|s,r| `cp #{s.path} #{r}`; puts "article template #{s.path}\n\t\t #{r}"}
10
+ s.add_action('.idx') {|s,r| `cp #{s.path} #{r}`; puts "index template #{s.path}\n\t\t #{r}"}
11
+ s.add_action('.png') {|s,r| `cp #{s.path} #{r}`; puts "png copy #{s.path}\n\t\t #{r}"}
12
+ s.add_action('.css') {|s,r| `cp #{s.path} #{r}`; puts "css copy #{s.path}\n\t\t #{r}"}
13
+
14
+ DirEntry.new(swapout).traverse(s)
15
+
16
+ end
@@ -0,0 +1,45 @@
1
+ require 'sitebuilder/filesystem'
2
+ require 'sitebuilder/string'
3
+
4
+ module SiteBuilder
5
+
6
+ # I generate a target website directory from a source website directory
7
+ # using mapping rules that determine what should be done for each file
8
+ # of the source website directory.
9
+ class SiteGenerator < Traverser
10
+
11
+ # I set up the source website directory and the destination website directory.
12
+ def initialize(source, destination)
13
+ @swapout = source
14
+ @replace = destination
15
+ @actions = {}
16
+ @actions.default=Proc.new {|s,r| puts "default action source:#{s.path}\n\t\t destination:#{r}"}
17
+ end
18
+
19
+ # internal callback used to handle a source directory
20
+ def traverse_dir(dir_entry)
21
+ converted_path = dir_entry.path.clone
22
+ converted_path.substitute_prefix!(@swapout, @replace)
23
+ `mkdir #{converted_path}`
24
+ end
25
+
26
+ # internal callback used to handle a source file
27
+ def traverse_file(file_entry)
28
+ if !file_entry.path.index('.svn')
29
+ converted_path = file_entry.dirname
30
+ converted_path.substitute_prefix!(@swapout, @replace) #BUG? - no clone here?
31
+ @actions[file_entry.extname].call(file_entry, converted_path)
32
+ end
33
+ end
34
+
35
+ # Add an action for a given source file extension. The action is given by a block which
36
+ # will be executed when traversal finds a matching file system entry matching the
37
+ # extension. The block is takes 2 arguments, first the FsEntry representing the source
38
+ # file system entry, and the second, a String which is the path to the destination
39
+ # directory.
40
+ def add_action(extension, &proc)
41
+ @actions[extension]=proc
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,40 @@
1
+ class String
2
+
3
+ # return true iff string commences with target
4
+ def begins_with(target)
5
+ self[0,target.size]==target
6
+ end
7
+
8
+ # return true iff string ends with target
9
+ def ends_with(target)
10
+ target_size = target.size
11
+ target==self[-target_size,target_size]
12
+ end
13
+
14
+ # return true iff string ends with target
15
+ # Case insensitive version.
16
+ def case_ends_with(target)
17
+ target_size = target.size
18
+ target.upcase==self.upcase[-target_size,target_size]
19
+ end
20
+
21
+ # iff string begins_with prefix, replace it with new_prefix
22
+ def substitute_prefix!(prefix,new_prefix)
23
+ return self if prefix != self[0,prefix.size]
24
+ self[0,prefix.size] = new_prefix
25
+ self
26
+ end
27
+
28
+ # iff string begins_with prefix, return a clone on which the
29
+ # new_prefix has been substituted for prefix
30
+ def substitute_prefix(prefix,new_prefix)
31
+ result =self.clone
32
+ result.substitute_prefix!(prefix,new_prefix)
33
+ end
34
+
35
+ # returns substring to the righthand side of the rightmost occurrence of token
36
+ def rightmost_of_token(token)
37
+ rightmost_index = rindex(token)
38
+ rightmost_index.nil? ? '' : self[rightmost_index+1,size-rightmost_index]
39
+ end
40
+ end
@@ -0,0 +1,4 @@
1
+ require 'sitebuilder/array'
2
+ require 'sitebuilder/filesystem'
3
+ require 'sitebuilder/sitegenerator'
4
+ require 'sitebuilder/string'
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'sitebuilder/array'
4
+
5
+ class ArrayTest < Test::Unit::TestCase
6
+
7
+ def test_tail
8
+ assert_equal([],[].tail);
9
+ assert_equal([],[1].tail);
10
+ assert_equal([2],[1,2].tail);
11
+ assert_equal([2,3],[1,2,3].tail);
12
+ end
13
+
14
+ def test_each_until_empty
15
+ result = [].each_until {|i| true}
16
+ assert !result, "empty array should return false"
17
+ end
18
+
19
+ def test_each_until_found
20
+ unit = [1,2,3,4,5]
21
+ pass_count = 0
22
+ result = unit.each_until {|i| pass_count=pass_count+1; true if i==3}
23
+ assert_equal(true, result, 'should return true')
24
+ assert_equal(3, pass_count,'should have traversed elements 1, 2 and 3')
25
+ end
26
+
27
+ def test_each_until_not_found
28
+ unit = [1,2,33,4,5]
29
+ pass_count = 0
30
+ result = unit.each_until {|i| pass_count=pass_count+1; true if i==3}
31
+ assert_equal(false, result, 'should return false')
32
+ assert_equal(unit.length+1, pass_count,'should have traversed elements 1, 2 and 33')
33
+ end
34
+
35
+ end
@@ -0,0 +1 @@
1
+ My ctime is used for tests.
@@ -0,0 +1,104 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'mocha'
4
+ require 'sitebuilder/filesystem'
5
+
6
+ FS = File::SEPARATOR
7
+ TEST_DIRNAME = 'data'
8
+ TESTDATA_DIR = ENV['SANDBOX']+FS+'sitebuilder'+FS+'test'+FS+TEST_DIRNAME
9
+ TEST_FILENAME = 'testfile.txt'
10
+ TEST_FILEPATH = TESTDATA_DIR+FS+TEST_FILENAME
11
+
12
+ class FsEntryTest < Test::Unit::TestCase
13
+
14
+ def test_instances_and_equality
15
+ a_file = SiteBuilder::FsEntry.new('/this/that/foo.txt')
16
+ another_file = SiteBuilder::FsEntry.new('/this/that/foo.txt')
17
+ different_file=SiteBuilder::FsEntry.new('/this/that/other.txt')
18
+
19
+ assert_equal(another_file, a_file)
20
+ assert_equal(a_file, another_file)
21
+ assert_not_equal(a_file, different_file)
22
+ assert_not_equal(another_file, different_file)
23
+ end
24
+
25
+ def test_extname
26
+ assert_equal('.txt', SiteBuilder::FsEntry.new('/this/that/foo.txt').extname)
27
+ assert_equal('', SiteBuilder::FsEntry.new('/this/that/foo.').extname)
28
+ assert_equal('', SiteBuilder::FsEntry.new('/this/that/foo').extname)
29
+ assert_equal('', SiteBuilder::FsEntry.new('/this/that/.foo').extname)
30
+ end
31
+
32
+ def test_extnless
33
+ assert_equal('foo', SiteBuilder::FsEntry.new('/this/that/foo.txt').extnless)
34
+ assert_equal('foo.',SiteBuilder::FsEntry.new('/this/that/foo.').extnless)
35
+ assert_equal('foo', SiteBuilder::FsEntry.new('/this/that/foo').extnless)
36
+ assert_equal('.foo',SiteBuilder::FsEntry.new('/this/that/.foo').extnless)
37
+ end
38
+
39
+ def test_basename
40
+ assert_equal('foo.txt', SiteBuilder::FsEntry.new('/this/that/foo.txt').basename)
41
+ assert_equal('foo.txt', SiteBuilder::FsEntry.new('/foo.txt').basename)
42
+ end
43
+
44
+ def test_dirname
45
+ assert_equal('/this/that', SiteBuilder::FsEntry.new('/this/that/foo.txt').dirname)
46
+ assert_equal('/this/that', SiteBuilder::FsEntry.new('/this/that/foo/').dirname)
47
+ end
48
+
49
+ def test_ctime
50
+ assert_equal(File::ctime(TEST_FILEPATH), SiteBuilder::FsEntry.new(TEST_FILEPATH).ctime)
51
+ assert SiteBuilder::FsEntry.new(TEST_FILEPATH).ctime.to_i > 0
52
+ end
53
+
54
+ def test_size
55
+ assert_equal(28, SiteBuilder::FsEntry.new(TEST_FILEPATH).size)
56
+ end
57
+
58
+ def test_fs_entry_from
59
+ assert_equal SiteBuilder::FileEntry, SiteBuilder::FsEntry.fs_entry_from(TEST_FILEPATH).class
60
+ assert_equal SiteBuilder::DirEntry, SiteBuilder::FsEntry.fs_entry_from(TESTDATA_DIR).class
61
+ end
62
+
63
+ def test_instance_fs_entry_from
64
+ instance = SiteBuilder::FsEntry.fs_entry_from(TESTDATA_DIR)
65
+ file_result = instance.fs_entry_from(TEST_FILENAME)
66
+ dir_result = instance.fs_entry_from('.')
67
+
68
+ assert_equal instance.path+FS+TEST_FILENAME, file_result.path
69
+ assert_equal instance.path, dir_result.path
70
+ end
71
+
72
+ end
73
+
74
+ class FileEntryTest < Test::Unit::TestCase
75
+
76
+ def test_traverse
77
+ traverser = SiteBuilder::Traverser.new
78
+ traverser.expects(:traverse_file).once().with do |file|
79
+ SiteBuilder::FileEntry==file.class && 'testfile.txt'==file.basename
80
+ end
81
+ traverser.expects(:traverse_dir).never()
82
+
83
+ file = SiteBuilder::FsEntry.fs_entry_from(TEST_FILEPATH)
84
+ file.traverse(traverser)
85
+ end
86
+
87
+ end
88
+
89
+ class DirEntryTest < Test::Unit::TestCase
90
+
91
+ def test_traverse
92
+ traverser = SiteBuilder::Traverser.new
93
+ traverser.expects(:traverse_file).once().with do |file|
94
+ SiteBuilder::FileEntry==file.class && 'testfile.txt'==file.basename
95
+ end
96
+ traverser.expects(:traverse_dir).once().with do |dir|
97
+ SiteBuilder::DirEntry==dir.class && TESTDATA_DIR==dir.path
98
+ end
99
+
100
+ file = SiteBuilder::FsEntry.fs_entry_from(TESTDATA_DIR)
101
+ file.traverse(traverser)
102
+ end
103
+
104
+ end
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'sitebuilder/string'
4
+
5
+ class ArrayTest < Test::Unit::TestCase
6
+
7
+ def test_begins_with
8
+ assert "This is an arbitrary string.".begins_with('')
9
+ assert "This is an arbitrary string.".begins_with('T')
10
+ assert "This is an arbitrary string.".begins_with('This is ')
11
+ assert !("This is an arbitrary string.".begins_with('This xis '))
12
+ end
13
+
14
+ def test_ends_with
15
+ assert "This is an arbitrary string.".ends_with('ing.')
16
+ assert "This is an arbitrary string.".ends_with('')
17
+ assert !("This is an arbitrary string.".ends_with('ingx.'))
18
+ end
19
+
20
+ def test_case_ends_with
21
+ assert "This is an arbitrary string.".case_ends_with('ing.')
22
+ assert "This is an arbitrary string.".case_ends_with('')
23
+ assert !("This is an arbitrary string.".case_ends_with('ingx.'))
24
+
25
+ assert "This is an arbitrary striNg.".case_ends_with('ing.')
26
+ assert "This is an arbitrary string.".case_ends_with('iNg.')
27
+ assert !("This is an arbitrary string.".case_ends_with('ingX.'))
28
+ end
29
+
30
+ def test_substitute_prefix_exclamation
31
+ unit = "This is an arbitrary string."
32
+ unit.substitute_prefix!("This is an","This is a very")
33
+ assert_equal "This is a very arbitrary string.", unit
34
+ end
35
+
36
+ def test_substitute_prefix_exclamation_when_prefix_doesnt_match
37
+ unit = "The cat sat on the mat."
38
+ unit.substitute_prefix!("This is an","This is a very")
39
+ assert_equal "The cat sat on the mat.", unit
40
+ end
41
+
42
+ def test_substitute_prefix_exclamation_when_prefix_doesnt_match_and_string_empty
43
+ unit = ""
44
+ unit.substitute_prefix!("This is an","This is a very")
45
+ assert_equal "", unit
46
+ end
47
+
48
+ def test_substitute_prefix_exclamation_when_prefix_match_is_empty
49
+ unit = "This is an arbitrary string."
50
+ unit.substitute_prefix!("","This is a very")
51
+ assert_equal "This is a veryThis is an arbitrary string.", unit
52
+ end
53
+
54
+ ################
55
+
56
+ def test_substitute_prefix
57
+ unit = "This is an arbitrary string."
58
+ assert_equal "This is a very arbitrary string.", unit.substitute_prefix("This is an","This is a very")
59
+ assert_equal "This is an arbitrary string.", unit
60
+ end
61
+
62
+ def test_substitute_prefix_when_prefix_doesnt_match
63
+ unit = "The cat sat on the mat."
64
+ assert_equal "The cat sat on the mat.", unit.substitute_prefix("This is an","This is a very")
65
+ end
66
+
67
+ def test_substitute_prefix_when_prefix_doesnt_match_and_string_empty
68
+ assert_equal "", "".substitute_prefix("This is an","This is a very")
69
+ end
70
+
71
+ def test_substitute_prefix_when_prefix_match_is_empty
72
+ unit = "This is an arbitrary string."
73
+ assert_equal "This is a veryThis is an arbitrary string.", unit.substitute_prefix("","This is a very")
74
+ end
75
+
76
+ def test_rightmost_of_token
77
+ assert_equal 'ring.', "This is an arbitrary string.".rightmost_of_token('t')
78
+ assert_equal '', "This is an arbitrary string.".rightmost_of_token('x')
79
+ end
80
+
81
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sitebuilder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dafydd Rees
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-03 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Static website generator using rule-based translation.
17
+ email: os@greenbarsoft.co.uk
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - CHANGELOG
24
+ - COPYING
25
+ - README.rdoc
26
+ - TODO.rdoc
27
+ files:
28
+ - lib/sitebuilder/array.rb
29
+ - lib/sitebuilder/filesystem.rb
30
+ - lib/sitebuilder/gen.rb
31
+ - lib/sitebuilder/sitegenerator.rb
32
+ - lib/sitebuilder/string.rb
33
+ - lib/sitebuilder.rb
34
+ - test/array_test.rb
35
+ - test/data/testfile.txt
36
+ - test/filesystem_test.rb
37
+ - test/string_test.rb
38
+ - CHANGELOG
39
+ - COPYING
40
+ - README.rdoc
41
+ - TODO.rdoc
42
+ has_rdoc: true
43
+ homepage: http://www.greenbarsoft.co.uk/software/sitebuilder
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.5
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Static site generator
70
+ test_files:
71
+ - ./test/array_test.rb
72
+ - ./test/filesystem_test.rb
73
+ - ./test/string_test.rb