nf 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: