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 ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ debug.log
6
+ newfile.*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in nf.gemspec
4
+ gemspec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require 'nf'
5
+
6
+ NF.new.run(ARGV)
7
+
8
+ # vim: ft=ruby
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,2 @@
1
+ text: .txt
2
+ ruby: .rb
@@ -0,0 +1 @@
1
+ # New Ruby file
@@ -0,0 +1 @@
1
+ New text file...
data/lib/nf/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ class NF
2
+ VERSION = "0.0.3"
3
+ end
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: