nf 0.0.3
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.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/History.txt +17 -0
- data/LICENCE +19 -0
- data/README.txt +41 -0
- data/Rakefile +1 -0
- data/bin/nf +8 -0
- data/lib/nf.rb +267 -0
- data/lib/nf/templates/INDEX.yaml +2 -0
- data/lib/nf/templates/ruby +1 -0
- data/lib/nf/templates/text +1 -0
- data/lib/nf/version.rb +3 -0
- data/nf.gemspec +22 -0
- metadata +71 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/History.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
=== 0.0.3 / 2012-01-21
|
2
|
+
|
3
|
+
* Implemented --delete to delete a template
|
4
|
+
* Simplified CLI for storing a template
|
5
|
+
* Simplified aspects of the code
|
6
|
+
* Global templates stored in more appropriate directory
|
7
|
+
* Improved README.txt
|
8
|
+
* Added LICENCE file
|
9
|
+
* More documentation
|
10
|
+
|
11
|
+
=== 0.0.2 / 2012-01-21
|
12
|
+
|
13
|
+
* Removed debugging code (caused error because of missing require)
|
14
|
+
|
15
|
+
=== 0.0.1 / 2012-01-20
|
16
|
+
|
17
|
+
* Initial implementation (not released)
|
data/LICENCE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2005 Gavin Sinclair (gsinclair@gmail.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.txt
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
nf (VERSION) -- create new file from template
|
2
|
+
|
3
|
+
Examples:
|
4
|
+
|
5
|
+
nf text
|
6
|
+
-> Creates new text file (newfile.txt) in current directory.
|
7
|
+
|
8
|
+
nf text /path/to/directory
|
9
|
+
-> Creates new text file in given directory.
|
10
|
+
|
11
|
+
nf text /path/to/file
|
12
|
+
-> Creates new text file in same directory as given file.
|
13
|
+
|
14
|
+
nf --list (-l)
|
15
|
+
-> Lists file templates (e.g. "text" above)
|
16
|
+
|
17
|
+
nf --store word /path/to/template.docx (-s)
|
18
|
+
-> Stores a new template named "word", copying the given file
|
19
|
+
into ~/.nf/word and updating the database. A new Word file
|
20
|
+
can now be created with "nf word".
|
21
|
+
|
22
|
+
nf --delete word (-d, --remove)
|
23
|
+
-> Removes the template that was stored by the previous example,
|
24
|
+
both from the database and the filesystem.
|
25
|
+
|
26
|
+
Motivation:
|
27
|
+
|
28
|
+
MacOSX lacks the Windows Explorer right-click functionality to
|
29
|
+
create a new file in the currently browsed folder. That is annoying. With
|
30
|
+
'nf' and DTerm installed, from any finder directory I can open DTerm and type
|
31
|
+
"nf word" to create a new Microsoft Word file.
|
32
|
+
|
33
|
+
Templates:
|
34
|
+
|
35
|
+
nf has two "global" templates: 'text' and 'ruby'. Any local templates you
|
36
|
+
create with '--store' are saved in ~/.nf.
|
37
|
+
|
38
|
+
Project:
|
39
|
+
|
40
|
+
Homepage: http://github.com/gsinclair/nf
|
41
|
+
Licence: MIT
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/nf
ADDED
data/lib/nf.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'nf/version'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
# Usage: NF.new.run(ARGV)
|
6
|
+
class NF
|
7
|
+
# Nothing to initialize.
|
8
|
+
def initialize
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(args)
|
12
|
+
case word = args.shift
|
13
|
+
when nil, "-h", "--help" then usage
|
14
|
+
when "--list", "-l" then list(args)
|
15
|
+
when "--store", "-s" then store(args)
|
16
|
+
when "--delete", "-d" then delete(args)
|
17
|
+
when "--remove" then delete(args)
|
18
|
+
else create_new_file(word, args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def usage
|
23
|
+
usage = File.read( File.expand_path("../../README.txt", __FILE__) )
|
24
|
+
puts usage.gsub("VERSION", 'v' + NF::VERSION)
|
25
|
+
end
|
26
|
+
|
27
|
+
# --list or -l
|
28
|
+
def list(args)
|
29
|
+
unless args.empty?
|
30
|
+
puts "(Ignoring arguments)"
|
31
|
+
end
|
32
|
+
puts
|
33
|
+
puts "Global templates:"
|
34
|
+
TemplateDir.global.templates.each do |t|
|
35
|
+
puts " " + t.name.ljust(15) + "(#{t.extension})"
|
36
|
+
end
|
37
|
+
puts
|
38
|
+
puts "Local templates:"
|
39
|
+
TemplateDir.local.templates.each do |t|
|
40
|
+
puts " " + t.name.ljust(15) + " (#{t.extension})"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# --store or -s word /path/to/template.docx
|
45
|
+
def store(args)
|
46
|
+
# Need two arguments: template name, template path
|
47
|
+
abort "--store takes two arguments: name, path" unless args.size == 2
|
48
|
+
name, source_path = args.shift(2)
|
49
|
+
abort "Invalid name" unless name =~ /^\w+$/
|
50
|
+
abort "Can't read file: #{source_path}" unless File.readable?(source_path)
|
51
|
+
extension = File.extname(source_path)
|
52
|
+
abort "Path #{source_path} doesn't have an extension" if extension.empty?
|
53
|
+
TemplateDir.local.store(name, source_path)
|
54
|
+
# The above line is not bad, but it has a lot of error-checking and
|
55
|
+
# concomitant message-printing that ill befits the TemplateDir class.
|
56
|
+
# That stuff should be handled by this class. Consider relocating it.
|
57
|
+
end
|
58
|
+
|
59
|
+
# --delete or -d or --remove template_name
|
60
|
+
def delete(args)
|
61
|
+
abort "--store takes one argument: template_name" unless args.size == 1
|
62
|
+
name = args.shift
|
63
|
+
abort "Invalid name" unless name =~ /^\w+$/
|
64
|
+
TemplateDir.local.delete(name)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Arguments examples:
|
68
|
+
# nf word
|
69
|
+
# nf word /path/to/directory
|
70
|
+
# nf word /path/to/file
|
71
|
+
def create_new_file(template_name, args)
|
72
|
+
file_or_dir = args.shift
|
73
|
+
abort "Too many arguments provided; see `nf --help`" unless args.empty?
|
74
|
+
abort "Invalid template name" unless template_name =~ /^\w+$/
|
75
|
+
f = file_or_dir
|
76
|
+
target_directory =
|
77
|
+
if f.nil? then Dir.pwd
|
78
|
+
elsif File.file?(f) then File.dirname(f)
|
79
|
+
elsif File.directory?(f) then f
|
80
|
+
else Dir.pwd # raise exception?
|
81
|
+
end
|
82
|
+
template = TemplateDir.resolve(template_name)
|
83
|
+
if template.nil?
|
84
|
+
abort "Cannot locate template named #{template_name}"
|
85
|
+
end
|
86
|
+
target_path = File.join(target_directory, "newfile#{template.extension}")
|
87
|
+
FileUtils.cp(template.path, target_path)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
# Logical representation of a template: name, extension, directory.
|
92
|
+
# Can find out the path to the template file (needed for creating a new file).
|
93
|
+
class Template
|
94
|
+
attr_reader :name, :extension
|
95
|
+
def initialize(name, extension, directory)
|
96
|
+
@name, @extension, @directory = name, extension, directory
|
97
|
+
end
|
98
|
+
|
99
|
+
def path
|
100
|
+
File.join(@directory, @name)
|
101
|
+
end
|
102
|
+
end # class Template
|
103
|
+
|
104
|
+
# Encapsulates a template directory. Methods:
|
105
|
+
# #templates -> [Template]
|
106
|
+
# #template(name) -> Template or nil
|
107
|
+
# .global_templates -> [Template]
|
108
|
+
# .local_templates -> [Template]
|
109
|
+
# .resolve(name) -> Template or nil
|
110
|
+
class TemplateDir
|
111
|
+
def initialize(dir)
|
112
|
+
@directory = dir
|
113
|
+
unless File.directory?(dir)
|
114
|
+
abort "TemplateDir#initialize: '#{dir}' is not a directory!"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# TemplateDir class methods
|
119
|
+
|
120
|
+
def TemplateDir.local_dir
|
121
|
+
@local_dir ||=
|
122
|
+
begin
|
123
|
+
local_dir = File.expand_path("~/.nf")
|
124
|
+
begin
|
125
|
+
FileUtils.mkpath(local_dir)
|
126
|
+
index_path = File.join(local_dir, "INDEX.yaml")
|
127
|
+
unless File.exist?(index_path)
|
128
|
+
FileUtils.touch(index_path)
|
129
|
+
end
|
130
|
+
rescue Errno::EEXIST
|
131
|
+
abort "Couldn't create local config dir ~/.nf: file (not directory) exists"
|
132
|
+
end
|
133
|
+
local_dir
|
134
|
+
end
|
135
|
+
end
|
136
|
+
def TemplateDir.global_dir
|
137
|
+
File.expand_path("../nf/templates", __FILE__)
|
138
|
+
end
|
139
|
+
def TemplateDir.global
|
140
|
+
TemplateDir.new(global_dir)
|
141
|
+
end
|
142
|
+
def TemplateDir.local
|
143
|
+
TemplateDir.new(local_dir)
|
144
|
+
end
|
145
|
+
def TemplateDir.update_local_index(name, extension)
|
146
|
+
TemplateDir.local.update_index(name, extension)
|
147
|
+
rescue => e
|
148
|
+
puts "Failed to update local index"
|
149
|
+
puts e.inspect
|
150
|
+
end
|
151
|
+
# Returns the first Template object matching the given name. Looks in local
|
152
|
+
# templates before global ones. Returns nil if no such template exists.
|
153
|
+
def TemplateDir.resolve(template_name)
|
154
|
+
template = TemplateDir.local.template(template_name)
|
155
|
+
if template.nil?
|
156
|
+
template = TemplateDir.global.template(template_name)
|
157
|
+
end
|
158
|
+
template
|
159
|
+
end
|
160
|
+
|
161
|
+
# TemplateDir instance methods
|
162
|
+
|
163
|
+
def ==(other)
|
164
|
+
other.class == TemplateDir and
|
165
|
+
@directory == other.instance_variable_get(:@directory)
|
166
|
+
end
|
167
|
+
|
168
|
+
# E.g. store("word", "/path/to/file.docx")
|
169
|
+
# * copies the file to ~/.nf/word
|
170
|
+
# * updates the database ~/.nf/INDEX.yaml
|
171
|
+
def store(name, source_path)
|
172
|
+
abort "Can't store in global directory" if self == TemplateDir.global_dir
|
173
|
+
target_path = File.join(@directory, name)
|
174
|
+
extension = File.extname(source_path)
|
175
|
+
if File.readable?(target_path)
|
176
|
+
puts "Warning: about to overwrite template file"
|
177
|
+
puts " #{target_path}"
|
178
|
+
puts
|
179
|
+
backup = "/tmp/nf.template.#{name}"
|
180
|
+
begin
|
181
|
+
FileUtils.cp target_path, backup
|
182
|
+
puts "Its contents are backed up to #{backup}"
|
183
|
+
rescue
|
184
|
+
puts "Tried to back up to #{backup} but failed. Contents of template below."
|
185
|
+
puts
|
186
|
+
puts File.read(target_path)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
puts
|
190
|
+
begin
|
191
|
+
FileUtils.cp(source_path, target_path)
|
192
|
+
update_index(name, extension)
|
193
|
+
puts "Created file #{target_path}"
|
194
|
+
rescue
|
195
|
+
puts "Failed to create file: #{target_path}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# E.g. TemplateDir.local.delete("word")
|
200
|
+
# * Deletes file ~/.ns/word
|
201
|
+
# * Removes key "word" from database ~/.ns/INDEX.yaml
|
202
|
+
# Returns true if deleted, false if doesn't exist in database.
|
203
|
+
# In either case, it attempts to remove the file.
|
204
|
+
def delete(name)
|
205
|
+
data = index_data()
|
206
|
+
if data.key?(name)
|
207
|
+
data.delete(name)
|
208
|
+
write_index(data)
|
209
|
+
puts "Removed template #{name} from database #{index_path}"
|
210
|
+
remove_file(name, :verbose)
|
211
|
+
else
|
212
|
+
puts "Template named #{name} doesn't exist in local template database"
|
213
|
+
remove_file(name, :quiet)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def templates() @templates ||= _templates end
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
def index_path() @_ip ||= File.join(@directory, "INDEX.yaml") end
|
222
|
+
def index_data() YAML.load( File.read(index_path())) || Hash.new end
|
223
|
+
def write_index(data)
|
224
|
+
File.open(index_path(), 'w') { |f| f.write(YAML.dump(data)) }
|
225
|
+
end
|
226
|
+
def update_index(name, extension)
|
227
|
+
data = index_data() # hash: ruby => .rb, text => .txt, ...
|
228
|
+
data[name] = extension
|
229
|
+
write_index(data)
|
230
|
+
end
|
231
|
+
def _templates
|
232
|
+
data = index_data() # hash: ruby => .rb, text => .txt, ...
|
233
|
+
data.map { |name, ext|
|
234
|
+
Template.new(name, ext, @directory)
|
235
|
+
}
|
236
|
+
end
|
237
|
+
# Returns the Template of the given name that is located in the directory
|
238
|
+
# encapsulated by this TemplateDir, or nil if none such exists.
|
239
|
+
def template(name)
|
240
|
+
data = index_data()
|
241
|
+
ext = data[name]
|
242
|
+
if ext
|
243
|
+
template = Template.new(name, ext, @directory)
|
244
|
+
else
|
245
|
+
nil
|
246
|
+
end
|
247
|
+
end
|
248
|
+
# Mode must be :quiet or :verbose.
|
249
|
+
def remove_file(name, mode)
|
250
|
+
path = File.join(@directory, name)
|
251
|
+
if File.exist?(path) and File.writable?(path)
|
252
|
+
FileUtils.rm(path) rescue remove_file_error_message(path, mode)
|
253
|
+
else
|
254
|
+
remove_file_error_message(path, mode)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
def remove_file_error_message(path, mode)
|
258
|
+
case mode
|
259
|
+
when :quiet then nil
|
260
|
+
when :verbose
|
261
|
+
puts "Failed to delete file #{path}"
|
262
|
+
else abort "Invalid value for mode: #{mode}"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end # class TemplateDir
|
266
|
+
|
267
|
+
end # class NF
|
@@ -0,0 +1 @@
|
|
1
|
+
# New Ruby file
|
@@ -0,0 +1 @@
|
|
1
|
+
New text file...
|
data/lib/nf/version.rb
ADDED
data/nf.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "nf/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "nf"
|
7
|
+
s.version = NF::VERSION
|
8
|
+
s.authors = ["Gavin Sinclair"]
|
9
|
+
s.email = ["gsinclair@gmail.com"]
|
10
|
+
s.homepage = "http://www.github.com/gsinclair/nf"
|
11
|
+
s.summary = "Create new file from a template"
|
12
|
+
s.description = "Create new file from a template (small command-line utility)"
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
# s.add_runtime_dependency "rest-client"
|
21
|
+
s.add_development_dependency "bundler"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gavin Sinclair
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: &2156159980 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2156159980
|
25
|
+
description: Create new file from a template (small command-line utility)
|
26
|
+
email:
|
27
|
+
- gsinclair@gmail.com
|
28
|
+
executables:
|
29
|
+
- nf
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- Gemfile
|
35
|
+
- History.txt
|
36
|
+
- LICENCE
|
37
|
+
- README.txt
|
38
|
+
- Rakefile
|
39
|
+
- bin/nf
|
40
|
+
- lib/nf.rb
|
41
|
+
- lib/nf/templates/INDEX.yaml
|
42
|
+
- lib/nf/templates/ruby
|
43
|
+
- lib/nf/templates/text
|
44
|
+
- lib/nf/version.rb
|
45
|
+
- nf.gemspec
|
46
|
+
homepage: http://www.github.com/gsinclair/nf
|
47
|
+
licenses: []
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.8.10
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Create new file from a template
|
70
|
+
test_files: []
|
71
|
+
has_rdoc:
|