synqa 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -1,5 +1,5 @@
1
- lib/**/*.rb
2
- bin/*
3
- -
4
- features/**/*.feature
5
- LICENSE.txt
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile CHANGED
@@ -1,16 +1,16 @@
1
- source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
5
-
6
- gem "net-ssh", ">= 2.0"
7
- gem "net-scp", ">= 1.0"
8
-
9
- # Add dependencies to develop your gem here.
10
- # Include everything needed to run rake, tests, features, etc.
11
- group :development do
12
- gem "shoulda", ">= 0"
13
- gem "bundler", "~> 1.0.0"
14
- gem "jeweler", "~> 1.5.2"
15
- gem "rcov", ">= 0"
16
- end
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "net-ssh", ">= 2.0"
7
+ gem "net-scp", ">= 1.0"
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem "shoulda", ">= 0"
13
+ gem "bundler", "~> 1.0.0"
14
+ gem "jeweler", "~> 1.6.2"
15
+ gem "rcov", ">= 0"
16
+ end
@@ -2,8 +2,8 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  git (1.2.5)
5
- jeweler (1.5.2)
6
- bundler (~> 1.0.0)
5
+ jeweler (1.6.2)
6
+ bundler (~> 1.0)
7
7
  git (>= 1.2.5)
8
8
  rake
9
9
  net-scp (1.0.4)
@@ -14,11 +14,12 @@ GEM
14
14
  shoulda (2.11.3)
15
15
 
16
16
  PLATFORMS
17
+ ruby
17
18
  x86-mingw32
18
19
 
19
20
  DEPENDENCIES
20
21
  bundler (~> 1.0.0)
21
- jeweler (~> 1.5.2)
22
+ jeweler (~> 1.6.2)
22
23
  net-scp (>= 1.0)
23
24
  net-ssh (>= 2.0)
24
25
  rcov
@@ -1,43 +1,43 @@
1
- = synqa
2
-
3
- *Synqa* is a simple file syncing tool that works over SSH, and is designed
4
- primarily for maintaining static websites. It uses a secure hash function to
5
- determine which files don't need to be copied because the destination copy
6
- is already identical to the source copy.
7
-
8
- It is available as a Ruby gem.
9
-
10
- I wrote *synqa* for two main reasons:
11
-
12
- * I couldn't get *rsync* to work on the combination of Cygwin and my
13
- hosting provider, and the rsync error messages were not very informative.
14
- * It was an opportunity to learn about SSH and how to use SSH and SCP with Ruby.
15
-
16
- == Installation
17
-
18
- <code>gem install synqa</code>
19
-
20
- == Dependencies of *synqa* are:
21
-
22
- * Ruby 1.9.2
23
- * Ruby gems *net-ssh* and *net-scp*
24
-
25
- Optionally:
26
- * An external SSH client. I use *plink*.
27
- * An external SCP client. I use *pscp*.
28
-
29
- For some sample code, see <b>examples/synga-useage.rb</b> and <b>examples/sample-rakefile</b>.
30
-
31
- == Licence
32
-
33
- Synqa is licensed under the GNU General Public License version 3.
34
-
35
- == Notes and Issues
36
-
37
- * *Synqa* has not been tested (or even designed to work) with file names
38
- containing whitespace or non-ASCII characters. Typically this doesn't matter for
39
- many static websites, but it will reduce the tool's usefulness as a general purpose
40
- backup tool.
41
-
42
- * Currently *Synqa* does not provide authentication options, on the assumption that you
43
- will use Pageant (which automagically provides "presented" keys for specified user/host combinations).
1
+ = synqa
2
+
3
+ *Synqa* is a simple file syncing tool that works over SSH, and is designed
4
+ primarily for maintaining static websites. It uses a secure hash function to
5
+ determine which files don't need to be copied because the destination copy
6
+ is already identical to the source copy.
7
+
8
+ It is available as a Ruby gem.
9
+
10
+ I wrote *synqa* for two main reasons:
11
+
12
+ * I couldn't get *rsync* to work on the combination of Cygwin and my
13
+ hosting provider, and the rsync error messages were not very informative.
14
+ * It was an opportunity to learn about SSH and how to use SSH and SCP with Ruby.
15
+
16
+ == Installation
17
+
18
+ <code>gem install synqa</code>
19
+
20
+ == Dependencies of *synqa* are:
21
+
22
+ * Ruby 1.9.2
23
+ * Ruby gems *net-ssh* and *net-scp*
24
+
25
+ Optionally:
26
+ * An external SSH client. I use *plink*.
27
+ * An external SCP client. I use *pscp*.
28
+
29
+ For some sample code, see <b>examples/synga-useage.rb</b> and <b>examples/sample-rakefile</b>.
30
+
31
+ == Licence
32
+
33
+ Synqa is licensed under the GNU General Public License version 3.
34
+
35
+ == Notes and Issues
36
+
37
+ * *Synqa* has not been tested (or even designed to work) with file names
38
+ containing whitespace or non-ASCII characters. Typically this doesn't matter for
39
+ many static websites, but it will reduce the tool's usefulness as a general purpose
40
+ backup tool.
41
+
42
+ * Currently *Synqa* does not provide authentication options, on the assumption that you
43
+ will use Pageant (which automagically provides "presented" keys for specified user/host combinations).
data/Rakefile CHANGED
@@ -1,53 +1,53 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "synqa"
16
- gem.homepage = "http://www.1729.com/software/synqa/"
17
- gem.license = "GPLv3"
18
- gem.summary = %Q{Sync files from a local directory to a remote directory via SSH/SCP}
19
- gem.description = %Q{Sync files from a local directory to a remote directory via SSH/SCP}
20
- gem.email = "http://www.1729.com/email.html"
21
- gem.authors = ["Philip Dorrell"]
22
- # Include your dependencies below. Runtime dependencies are required when using your gem,
23
- # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
- # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
- # gem.add_development_dependency 'rspec', '> 1.2.3'
26
- end
27
- Jeweler::RubygemsDotOrgTasks.new
28
-
29
- require 'rake/testtask'
30
- Rake::TestTask.new(:test) do |test|
31
- test.libs << 'lib' << 'test'
32
- test.pattern = 'test/**/test_*.rb'
33
- test.verbose = true
34
- end
35
-
36
- require 'rcov/rcovtask'
37
- Rcov::RcovTask.new do |test|
38
- test.libs << 'test'
39
- test.pattern = 'test/**/test_*.rb'
40
- test.verbose = true
41
- end
42
-
43
- task :default => :test
44
-
45
- require 'rake/rdoctask'
46
- Rake::RDocTask.new do |rdoc|
47
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
-
49
- rdoc.rdoc_dir = 'rdoc'
50
- rdoc.title = "synqa #{version}"
51
- rdoc.rdoc_files.include('README*')
52
- rdoc.rdoc_files.include('lib/**/*.rb')
53
- end
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "synqa"
16
+ gem.homepage = "http://www.1729.com/software/synqa/"
17
+ gem.license = "GPLv3"
18
+ gem.summary = %Q{Sync files from a local directory to a remote directory via SSH/SCP}
19
+ gem.description = %Q{Synqa syncs files from a local directory to a remote directory using an SSH connection. It is designed for uploading a static website. It determines if existing files need to be updated by calculating cryptographic hashes on the remote server.}
20
+ gem.email = "http://www.1729.com/email.html"
21
+ gem.authors = ["Philip Dorrell"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+
36
+ require 'rcov/rcovtask'
37
+ Rcov::RcovTask.new do |test|
38
+ test.libs << 'test'
39
+ test.pattern = 'test/**/test_*.rb'
40
+ test.verbose = true
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "synqa #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -1,9 +1,9 @@
1
-
2
- (load-this-project
3
- `( (:ruby-executable ,*ruby-1.9-executable*)
4
- (:run-project-command (ruby-run-file ,(concat (project-base-directory) "RunMain.rb")))
5
- (:build-function project-compile-with-command)
6
- (:compile-command "rake")
7
- (:ruby-args ("-I."))
8
- ) )
9
-
1
+
2
+ (load-this-project
3
+ `( (:ruby-executable ,*ruby-1.9-executable*)
4
+ (:run-project-command (ruby-run-file ,(concat (project-base-directory) "RunMain.rb")))
5
+ (:build-function project-compile-with-command)
6
+ (:compile-command "rake")
7
+ (:ruby-args (,(concat "-I" (project-base-directory) "lib")))
8
+ ) )
9
+
@@ -1,37 +1,37 @@
1
- # Sample code for synqa useage -- you will need to fill in your own details
2
-
3
- require 'synqa.rb'
4
- require 'based'
5
- require 'digest/sha2'
6
-
7
- STDOUT.sync = true
8
-
9
- include Synqa
10
- sha256Sum = Sha256SumCommand.new() # sha256sum (with 2 characters between hash and file name)
11
- sha256 = Sha256Command.new() # sha256 -r (with 1 space between hash and file name)
12
-
13
- # Default is use Ruby net-ssh & net-scp gems, the following line defines external SSH/SCP applications
14
- # plinkAndPscp = ExternalSshScp.new("plink", "pscp")
15
-
16
- localContentLocation = LocalContentLocation.new(Based::BaseDirectory.new("c:/dev/src/project"),
17
- Digest::SHA256,
18
- "c:/temp/synqa/local.project.content.cache.txt")
19
- # Default uses Ruby net-ssh & net-scp gems
20
- remoteHost = SshContentHost.new("username@host.example.com", sha256)
21
-
22
- # Alternative uses plink & pscp
23
- #remoteHost = SshContentHost.new("username@host.example.com", sha256, ExternalSshScp)
24
-
25
-
26
- # Note: the specification of plink & pscp assumes that keys are managed with Pageant, and therefore
27
- # do not need to be specified on the command line.
28
-
29
- remoteContentLocation = RemoteContentLocation.new(remoteHost,
30
- "/home/username/public",
31
- "c:/temp/synqa/remote.project.content.cache.txt")
32
-
33
- # Note: the cache files are currently written, but not yet used to speed up the sync
34
-
35
- syncOperation = SyncOperation.new(localContentLocation, remoteContentLocation)
36
-
37
- syncOperation.doSync(:dryRun => true) # set to false to make it actually happen
1
+ # Sample code for synqa useage -- you will need to fill in your own details
2
+
3
+ require 'synqa.rb'
4
+ require 'based'
5
+ require 'digest/sha2'
6
+
7
+ STDOUT.sync = true
8
+
9
+ include Synqa
10
+ sha256Sum = Sha256SumCommand.new() # sha256sum (with 2 characters between hash and file name)
11
+ sha256 = Sha256Command.new() # sha256 -r (with 1 space between hash and file name)
12
+
13
+ # Default is use Ruby net-ssh & net-scp gems, the following line defines external SSH/SCP applications
14
+ # plinkAndPscp = ExternalSshScp.new("plink", "pscp")
15
+
16
+ localContentLocation = LocalContentLocation.new(Based::BaseDirectory.new("c:/dev/src/project"),
17
+ Digest::SHA256,
18
+ "c:/temp/synqa/local.project.content.cache.txt")
19
+ # Default uses Ruby net-ssh & net-scp gems
20
+ remoteHost = SshContentHost.new("username@host.example.com", sha256)
21
+
22
+ # Alternative uses plink & pscp
23
+ #remoteHost = SshContentHost.new("username@host.example.com", sha256, ExternalSshScp)
24
+
25
+
26
+ # Note: the specification of plink & pscp assumes that keys are managed with Pageant, and therefore
27
+ # do not need to be specified on the command line.
28
+
29
+ remoteContentLocation = RemoteContentLocation.new(remoteHost,
30
+ "/home/username/public",
31
+ "c:/temp/synqa/remote.project.content.cache.txt")
32
+
33
+ # Note: the cache files are currently written, but not yet used to speed up the sync
34
+
35
+ syncOperation = SyncOperation.new(localContentLocation, remoteContentLocation)
36
+
37
+ syncOperation.doSync(:dryRun => true) # set to false to make it actually happen
@@ -1,177 +1,177 @@
1
- # The Based module supports the concept of a "based" directory. Typically in modern software development,
2
- # a project is represented by a set of sub-directories and files within a base directory. The exact location
3
- # of the base directory is not so important (i.e. it's wherever you checked it out of source control). For a given
4
- # file or sub-directory, one is often more interested in the path relative to the base directory, rather than
5
- # the absolute path. (But you still need the full path when performing an actual file operation on the file
6
- # or directory.)
7
- # Also, there might be files and directories that you wish to ignore. Based supports simple functional includes and
8
- # excludes. (A bit less succint than include/exclude globs, but probably more flexible.)
9
-
10
- module Based
11
-
12
- # A base class for directories: i.e. either the base directory itself, or a sub-directory
13
- class Directory
14
- # The base directory (object)
15
- attr_reader :base
16
-
17
- # The path of this directory relative to the base directory (includes a following "/" if non-empty)
18
- attr_reader :relativePath
19
-
20
- # The elements of the relative path as an array
21
- attr_reader :pathElements
22
-
23
- # The immediate name of the directory (nil for the base directory)
24
- attr_reader :name
25
-
26
- # The parent directory (nil for the base directory)
27
- attr_reader :parent
28
-
29
- # The full path of the file
30
- attr_reader :fullPath
31
-
32
- # initialise with un-initialised entries
33
- def initialize
34
- @entries = nil
35
- end
36
-
37
- # get the "entries", i.e. list of files and directories immediately contained in this directory, and cache them.
38
- # Note that dirExclude, fileInclude and fileExclude functions in the base directory object
39
- # may be applied to filter out some entries.
40
- def getEntries
41
- if @entries == nil
42
- @entries = Dir.entries(fullPath)
43
- @dirs = []
44
- @files = []
45
- for entry in @entries
46
- if entry != "." and entry != ".."
47
- fullEntryPath = fullPath + entry
48
- if ::File.directory?(fullEntryPath)
49
- subDirectory = SubDirectory.new(entry, self)
50
- if @base.dirExclude == nil or not @base.dirExclude.call(subDirectory)
51
- @dirs << subDirectory
52
- end
53
- elsif ::File.file?(fullEntryPath)
54
- file = File.new(entry, self)
55
- if @base.fileInclude == nil or @base.fileInclude.call(file)
56
- if @base.fileExclude == nil or not @base.fileExclude.call(file)
57
- @files << file
58
- end
59
- end
60
- end
61
- end
62
- end
63
- @dirs.sort_by! {|dir| dir.name}
64
- @files.sort_by! {|file| file.name}
65
- end
66
- end
67
-
68
- # Get list of files immediately contained in this directory
69
- def files
70
- getEntries()
71
- return @files
72
- end
73
-
74
- # Get list of directories immediately contained in this directory
75
- def dirs
76
- getEntries()
77
- return @dirs
78
- end
79
-
80
- # Get a list of all the sub-directories of this directory (with parents preceding children)
81
- def subDirs
82
- result = []
83
- for dir in dirs
84
- result << dir
85
- result += dir.subDirs
86
- end
87
- return result
88
- end
89
-
90
- # Get a list of all files contained within this directory
91
- def allFiles
92
- result = files
93
- for subDir in subDirs
94
- result += subDir.files
95
- end
96
- return result
97
- end
98
- end
99
-
100
- # An object representing a sub-directory (i.e. not the base directory itself)
101
- class SubDirectory<Directory
102
-
103
- # Construct directory object from parent directory object and this directory's name
104
- def initialize(name, parent)
105
- super()
106
- @name = name
107
- @parent = parent
108
- @base = @parent.base
109
- @relativePath = @parent.relativePath + @name + "/"
110
- @pathElements = @parent.pathElements + [name]
111
- @fullPath = @parent.fullPath + @name + "/"
112
- end
113
- end
114
-
115
- # An object representing the base directory
116
- class BaseDirectory<Directory
117
-
118
- # Function to decide if files should be included (if nil, assume all included). Subject
119
- # to exclusion by fileExclude
120
- attr_reader :fileInclude
121
-
122
- # Function to decide if files should be excluded
123
- attr_reader :fileExclude
124
-
125
- # Function to decide if sub-directories should be excluded (if a directory is excluded, so
126
- # are all it's sub-directories and files contained within)
127
- attr_reader :dirExclude
128
-
129
- # Initialise from absolute file path. Options include :dirExclude, :fileInclude and :fileExclude
130
- def initialize(path, options = {})
131
- super()
132
- @name = nil
133
- @parent = nil
134
- @base = self
135
- @relativePath = ""
136
- @pathElements = []
137
- @fullPath = path.end_with?("/") ? path : path + "/"
138
- @dirExclude = options.fetch(:dirExclude, nil)
139
- @fileInclude = options.fetch(:fileInclude, nil)
140
- @fileExclude = options.fetch(:fileExclude, nil)
141
- end
142
- end
143
-
144
- # An object representing a file within the base directory
145
- class File
146
- # immediate name of file
147
- attr_reader :name
148
-
149
- # parent, i.e. containing directory
150
- attr_reader :parent
151
-
152
- # the base directory
153
- attr_reader :base
154
-
155
- # path of this file relative to base directory
156
- attr_reader :relativePath
157
-
158
- # elements of file path (including the name) as an array
159
- attr_reader :pathElements
160
-
161
- # full absolute path name of file
162
- attr_reader :fullPath
163
-
164
- # initialise from name and containing directory
165
- def initialize(name, parent)
166
- super()
167
- @name = name
168
- @parent = parent
169
- @base = @parent.base
170
- @relativePath = @parent.relativePath + @name
171
- @pathElements = @parent.pathElements + [name]
172
- @fullPath = @parent.fullPath + @name
173
- end
174
-
175
- end
176
-
177
- end
1
+ # The Based module supports the concept of a "based" directory. Typically in modern software development,
2
+ # a project is represented by a set of sub-directories and files within a base directory. The exact location
3
+ # of the base directory is not so important (i.e. it's wherever you checked it out of source control). For a given
4
+ # file or sub-directory, one is often more interested in the path relative to the base directory, rather than
5
+ # the absolute path. (But you still need the full path when performing an actual file operation on the file
6
+ # or directory.)
7
+ # Also, there might be files and directories that you wish to ignore. Based supports simple functional includes and
8
+ # excludes. (A bit less succint than include/exclude globs, but probably more flexible.)
9
+
10
+ module Based
11
+
12
+ # A base class for directories: i.e. either the base directory itself, or a sub-directory
13
+ class Directory
14
+ # The base directory (object)
15
+ attr_reader :base
16
+
17
+ # The path of this directory relative to the base directory (includes a following "/" if non-empty)
18
+ attr_reader :relativePath
19
+
20
+ # The elements of the relative path as an array
21
+ attr_reader :pathElements
22
+
23
+ # The immediate name of the directory (nil for the base directory)
24
+ attr_reader :name
25
+
26
+ # The parent directory (nil for the base directory)
27
+ attr_reader :parent
28
+
29
+ # The full path of the file
30
+ attr_reader :fullPath
31
+
32
+ # initialise with un-initialised entries
33
+ def initialize
34
+ @entries = nil
35
+ end
36
+
37
+ # get the "entries", i.e. list of files and directories immediately contained in this directory, and cache them.
38
+ # Note that dirExclude, fileInclude and fileExclude functions in the base directory object
39
+ # may be applied to filter out some entries.
40
+ def getEntries
41
+ if @entries == nil
42
+ @entries = Dir.entries(fullPath)
43
+ @dirs = []
44
+ @files = []
45
+ for entry in @entries
46
+ if entry != "." and entry != ".."
47
+ fullEntryPath = fullPath + entry
48
+ if ::File.directory?(fullEntryPath)
49
+ subDirectory = SubDirectory.new(entry, self)
50
+ if @base.dirExclude == nil or not @base.dirExclude.call(subDirectory)
51
+ @dirs << subDirectory
52
+ end
53
+ elsif ::File.file?(fullEntryPath)
54
+ file = File.new(entry, self)
55
+ if @base.fileInclude == nil or @base.fileInclude.call(file)
56
+ if @base.fileExclude == nil or not @base.fileExclude.call(file)
57
+ @files << file
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ @dirs.sort_by! {|dir| dir.name}
64
+ @files.sort_by! {|file| file.name}
65
+ end
66
+ end
67
+
68
+ # Get list of files immediately contained in this directory
69
+ def files
70
+ getEntries()
71
+ return @files
72
+ end
73
+
74
+ # Get list of directories immediately contained in this directory
75
+ def dirs
76
+ getEntries()
77
+ return @dirs
78
+ end
79
+
80
+ # Get a list of all the sub-directories of this directory (with parents preceding children)
81
+ def subDirs
82
+ result = []
83
+ for dir in dirs
84
+ result << dir
85
+ result += dir.subDirs
86
+ end
87
+ return result
88
+ end
89
+
90
+ # Get a list of all files contained within this directory
91
+ def allFiles
92
+ result = files
93
+ for subDir in subDirs
94
+ result += subDir.files
95
+ end
96
+ return result
97
+ end
98
+ end
99
+
100
+ # An object representing a sub-directory (i.e. not the base directory itself)
101
+ class SubDirectory<Directory
102
+
103
+ # Construct directory object from parent directory object and this directory's name
104
+ def initialize(name, parent)
105
+ super()
106
+ @name = name
107
+ @parent = parent
108
+ @base = @parent.base
109
+ @relativePath = @parent.relativePath + @name + "/"
110
+ @pathElements = @parent.pathElements + [name]
111
+ @fullPath = @parent.fullPath + @name + "/"
112
+ end
113
+ end
114
+
115
+ # An object representing the base directory
116
+ class BaseDirectory<Directory
117
+
118
+ # Function to decide if files should be included (if nil, assume all included). Subject
119
+ # to exclusion by fileExclude
120
+ attr_reader :fileInclude
121
+
122
+ # Function to decide if files should be excluded
123
+ attr_reader :fileExclude
124
+
125
+ # Function to decide if sub-directories should be excluded (if a directory is excluded, so
126
+ # are all it's sub-directories and files contained within)
127
+ attr_reader :dirExclude
128
+
129
+ # Initialise from absolute file path. Options include :dirExclude, :fileInclude and :fileExclude
130
+ def initialize(path, options = {})
131
+ super()
132
+ @name = nil
133
+ @parent = nil
134
+ @base = self
135
+ @relativePath = ""
136
+ @pathElements = []
137
+ @fullPath = path.end_with?("/") ? path : path + "/"
138
+ @dirExclude = options.fetch(:dirExclude, nil)
139
+ @fileInclude = options.fetch(:fileInclude, nil)
140
+ @fileExclude = options.fetch(:fileExclude, nil)
141
+ end
142
+ end
143
+
144
+ # An object representing a file within the base directory
145
+ class File
146
+ # immediate name of file
147
+ attr_reader :name
148
+
149
+ # parent, i.e. containing directory
150
+ attr_reader :parent
151
+
152
+ # the base directory
153
+ attr_reader :base
154
+
155
+ # path of this file relative to base directory
156
+ attr_reader :relativePath
157
+
158
+ # elements of file path (including the name) as an array
159
+ attr_reader :pathElements
160
+
161
+ # full absolute path name of file
162
+ attr_reader :fullPath
163
+
164
+ # initialise from name and containing directory
165
+ def initialize(name, parent)
166
+ super()
167
+ @name = name
168
+ @parent = parent
169
+ @base = @parent.base
170
+ @relativePath = @parent.relativePath + @name
171
+ @pathElements = @parent.pathElements + [name]
172
+ @fullPath = @parent.fullPath + @name
173
+ end
174
+
175
+ end
176
+
177
+ end