wordsmith 0.0.1

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.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ .rvmrc
4
+ .DS_Store
5
+ example
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ wordsmith (0.0.1)
5
+ kindlegen
6
+ nokogiri
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ kindlegen (2.3.1)
12
+ systemu
13
+ nokogiri (1.5.2)
14
+ rake (0.9.2.2)
15
+ systemu (2.5.0)
16
+ test-unit (2.4.8)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ rake
23
+ test-unit
24
+ wordsmith!
@@ -0,0 +1,69 @@
1
+ ## What is it about?
2
+
3
+ Wordsmith is a ruby Framework with a built in command-line toolset to
4
+ create, share, publish and collaborate on e-books, guides, manuals, etc.
5
+
6
+ Through the command line interface, you can create a project repository
7
+ with a standard directory structure that allows you and your co-authors
8
+ to easily manage your content.
9
+
10
+ The command line interface provides a set of commands to export
11
+ in the following formats:
12
+
13
+ * epub
14
+ * mobi
15
+ * pdf
16
+ * html
17
+
18
+ You can also publish your book to your github project page with a single command.
19
+
20
+ ## Installing
21
+
22
+ First of all, you should have [Pandoc][pandoc] installed in your system.
23
+
24
+ [pandoc]: http://johnmacfarlane.net/pandoc/installing.html
25
+
26
+ Next, install the wordsmith ruby gem.
27
+
28
+ gem install wordsmith
29
+
30
+ Now you're good to go.
31
+
32
+ ## Usage
33
+
34
+ Wordsmith uses the following directory structure
35
+
36
+ book/
37
+ layout/
38
+ header.html
39
+ footer.html
40
+ assets/
41
+ images/
42
+ cover.jpg
43
+ stylesheets/
44
+ default.css
45
+ book.css
46
+ content/
47
+ 01_chapter_one.md
48
+ 02_chapter_two.md
49
+ 03_chapter_three/
50
+ 01_lorem.md
51
+ 02_ipsum.md
52
+ final/
53
+ book.epub
54
+ book.mobi
55
+ book.pdf
56
+ book_html/
57
+ Makefile
58
+
59
+ The **layout** directory contains the header and footer for an online version of your book.
60
+
61
+ The **assets** directory contains images and stylesheets.
62
+
63
+ In the **content** directory you can:
64
+
65
+ 1. write content as single files
66
+ * use directories to easily manage long chapters
67
+
68
+ **Please note** that the file names in the 'content' folder must be snake cased and begin with the number of
69
+ the chapter or section (e.g. 01_chapter_one.md).
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
3
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
4
+
5
+ begin
6
+ # resolve bin path, ignoring symlinks
7
+ require "pathname"
8
+ bin_file = Pathname.new(__FILE__).realpath
9
+
10
+ # add self to libpath
11
+ $:.unshift File.expand_path("../../lib", bin_file)
12
+
13
+ require 'wordsmith'
14
+
15
+ # start up the CLI
16
+ require 'wordsmith/cli'
17
+ Wordsmith.new.run
18
+ rescue Interrupt
19
+ puts("\n ! Command cancelled.")
20
+ end
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env ruby -w
2
+ ######################################
3
+ # A tiny wrapper over optparse that gives easy subcommand facility.
4
+ # It also neatly prints help for global and subcommands
5
+ # as well as summarizes subcommands in global help.
6
+ #
7
+ # Thanks to Robert Klemme for his idea on lazy loading the subcommand option parsers.
8
+ #
9
+ # @author Rahul Kumar, Jun 2010
10
+ # @date 2010-06-20 22:33
11
+ #
12
+ # @examples
13
+ # if a program has subcommands foo and baz
14
+ #
15
+ # ruby subcommand.rb help
16
+ # ruby subcommand.rb --help
17
+ # ruby subcommand.rb help foo
18
+ # ruby subcommand.rb foo --help
19
+ # ruby subcommand.rb baz --quiet "some text"
20
+ # ruby subcommand.rb --verbose foo --force file.zzz
21
+ #
22
+ # == STEPS
23
+ # 1. define global_options (optional)
24
+ #
25
+ # global_options do |opts|
26
+ # opts.banner = "Usage: #{$0} [options] [subcommand [options]]"
27
+ # opts.description = "Stupid program that does something"
28
+ # opts.separator ""
29
+ # opts.separator "Global options are:"
30
+ # opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
31
+ # options[:verbose] = v
32
+ # end
33
+ # end
34
+ #
35
+ # 2. define commands using command().
36
+ # command :foo do |opts|
37
+ # opts.banner = "Usage: foo [options]"
38
+ # opts.description = "desc for foo"
39
+ # opts.on("-f", "--[no-]force", "force verbosely") do |v|
40
+ # options[:force] = v
41
+ # end
42
+ # end
43
+ #
44
+ # 3. call opt_parse()
45
+ #
46
+ # 4. As before, handle ARGS and options hash.
47
+ #
48
+ # TODO: add aliases for commands
49
+ ######################################
50
+ require 'optparse'
51
+
52
+ # Allow command to have a description to generate help
53
+ class OptionParser
54
+ attr_accessor :description
55
+ #attr_accessor :action
56
+ end
57
+
58
+ module Subcommands
59
+ ##
60
+ # specify a single command and all its options
61
+ # If multiple names are given, they are treated as aliases.
62
+ # Do repeatedly for each command
63
+ # Yields the optionparser
64
+ def command *names
65
+ name = names.shift
66
+ @commands ||= {}
67
+ @aliases ||= {}
68
+ if names.length > 0
69
+ names.each do |n|
70
+ #puts "aliases #{n} => #{name} "
71
+ @aliases[n.to_s] = name.to_s
72
+ end
73
+ end
74
+ # Thanks to Robert Klemme for the lazy loading idea.
75
+ opt = lambda { OptionParser.new do |opts|
76
+ yield opts
77
+ # append desc to banner in next line
78
+ opts.banner << "\n#{opts.description}\n" if opts.description
79
+ end }
80
+ @commands[name.to_s] = opt
81
+ end
82
+ # specify global options and banner and description
83
+ # Yields the optionparser
84
+ def global_options
85
+ if !defined? @global
86
+ @global = OptionParser.new do |opts|
87
+ yield opts
88
+ end
89
+ else
90
+ yield @global
91
+ end
92
+ end
93
+
94
+ # Added so applications can print out a bare listing of top level commands
95
+ # for dynamic custom completion.
96
+ def list_actions
97
+ @commands.each_pair do |c, opt| puts c
98
+ end
99
+ end
100
+
101
+ def print_actions
102
+ cmdtext = "Commands are:"
103
+ @commands.each_pair do |c, opt|
104
+ #puts "inside opt.call loop"
105
+ desc = opt.call.description
106
+ cmdtext << "\n #{c} : #{desc}"
107
+ end
108
+
109
+ # print aliases
110
+ unless @aliases.empty?
111
+ cmdtext << "\n\nAliases: \n"
112
+ @aliases.each_pair { |name, val| cmdtext << " #{name} - #{val}\n" }
113
+ end
114
+
115
+ cmdtext << "\n\nSee '#{$0} help COMMAND' for more information on a specific command."
116
+ end
117
+ ## add text of subcommands in help and --help option
118
+ def add_subcommand_help
119
+ # user has defined some, but lets add subcommand information
120
+
121
+ cmdtext = print_actions
122
+
123
+ global_options do |opts|
124
+ # lets add the description user gave into banner
125
+ opts.banner << "\n#{opts.description}\n" if opts.description
126
+ opts.separator ""
127
+ opts.separator cmdtext
128
+ end
129
+ end
130
+ # this is so that on pressing --help he gets same subcommand help as when doing help.
131
+ # This is to be added in your main program, after defining global options
132
+ # if you want detailed help on --help. This is since optionparser's default
133
+ # --help will not print your actions/commands
134
+ def add_help_option
135
+ global_options do |opts|
136
+ opts.on("-h", "--help", "Print this help") do |v|
137
+ add_subcommand_help
138
+ puts @global
139
+ exit
140
+ end
141
+ end
142
+ end
143
+ # first parse global optinos
144
+ # then parse subcommand options if valid subcommand
145
+ # special case of "help command" so we print help of command - git style (3)
146
+ # in all invalid cases print global help
147
+ # @return command name if relevant
148
+ def opt_parse
149
+ # if user has not defined global, we need to create it
150
+ @command_name = nil
151
+ if !defined? @global
152
+ global_options do |opts|
153
+ opts.banner = "Usage: #{$0} [options] [subcommand [options]]"
154
+ opts.separator ""
155
+ opts.separator "Global options are:"
156
+ opts.on("-h", "--help", "Print this help") do |v|
157
+ add_subcommand_help
158
+ puts @global
159
+ exit
160
+ end
161
+ opts.separator ""
162
+ #opts.separator subtext # FIXME: no such variable supposed to have subcommand help
163
+ end
164
+ else
165
+ end
166
+ @global.order!
167
+ cmd = ARGV.shift
168
+ if cmd
169
+ #$stderr.puts "Command: #{cmd}, args:#{ARGV}, #{@commands.keys} "
170
+ sc = @commands[cmd]
171
+ #puts "sc: #{sc}: #{@commands}"
172
+ unless sc
173
+ # see if an alias exists
174
+ sc, cmd = _check_alias cmd
175
+ end
176
+ # if valid command parse the args
177
+ if sc
178
+ @command_name = cmd
179
+ sc.call.order!
180
+ else
181
+ # else if help <command> then print its help GIT style (3)
182
+ if !ARGV.empty? && cmd == "help"
183
+ cmd = ARGV.shift
184
+ #$stderr.puts " 110 help #{cmd}"
185
+ sc = @commands[cmd]
186
+ # if valid command print help, else print global help
187
+ unless sc
188
+ sc, cmd = _check_alias cmd
189
+ end
190
+ if sc
191
+ #puts " 111 help #{cmd}"
192
+ puts sc.call
193
+ else
194
+ # no help for this command XXX check for alias
195
+ puts "Invalid command: #{cmd}."
196
+ add_subcommand_help
197
+ puts @global
198
+ end
199
+ else
200
+ # invalid command
201
+ puts "Invalid command: #{cmd}" unless cmd == "help"
202
+ add_subcommand_help
203
+ puts @global
204
+ end
205
+ exit 0
206
+ end
207
+ end
208
+ return @command_name
209
+ end
210
+ def alias_command name, *args
211
+ @aliases[name.to_s] = args
212
+ end
213
+ def _check_alias cmd
214
+ alas = @aliases[cmd]
215
+ #$stderr.puts "195 alas: #{alas} "
216
+ if alas
217
+ case alas
218
+ when Array
219
+ cmd = alas.shift
220
+ #$stderr.puts "Array cmd: #{cmd} "
221
+ ARGV.unshift alas.shift unless alas.empty?
222
+ #$stderr.puts "ARGV #{ARGV} "
223
+ else
224
+ cmd = alas
225
+ end
226
+ end
227
+ sc = @commands[cmd] if cmd
228
+ return sc, cmd
229
+ end
230
+ end
231
+
232
+ if __FILE__ == $PROGRAM_NAME
233
+ include Subcommands
234
+ options = {}
235
+ appname = File.basename($0)
236
+ # global is optional
237
+ global_options do |opts|
238
+ opts.banner = "Usage: #{appname} [options] [subcommand [options]]"
239
+ opts.description = "Stupid program that does something"
240
+ opts.separator ""
241
+ opts.separator "Global options are:"
242
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
243
+ options[:verbose] = v
244
+ end
245
+ end
246
+ add_help_option
247
+ # define a command
248
+ command :foo, :goo do |opts|
249
+ opts.banner = "Usage: foo [options]"
250
+ opts.description = "desc for foo"
251
+ opts.on("-f", "--[no-]force", "force verbosely") do |v|
252
+ options[:force] = v
253
+ end
254
+ end
255
+ command :baz do |opts|
256
+ opts.banner = "Usage: baz [options]"
257
+ opts.description = "desc for baz"
258
+ opts.on("-q", "--[no-]quiet", "quietly run ") do |v|
259
+ options[:quiet] = v
260
+ end
261
+ end
262
+ alias_command :bar, 'baz'
263
+ alias_command :boo, 'foo', '--force'
264
+ alias_command :zoo, 'foo', 'ruby'
265
+
266
+ # do the parsing.
267
+ cmd = opt_parse()
268
+
269
+ puts "cmd: #{cmd}"
270
+ puts "options ......"
271
+ p options
272
+ puts "ARGV:"
273
+ p ARGV
274
+ end
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'nokogiri'
3
+ require 'kindlegen'
4
+ require 'yaml'
5
+ require 'git'
6
+
7
+ require 'wordsmith/init'
8
+ require 'wordsmith/generate'
9
+ require 'wordsmith/publish'
10
+
11
+ require 'fileutils'
12
+ require 'pp'
13
+
14
+ class Wordsmith
15
+ include Init
16
+ include Generate
17
+ include Publish
18
+
19
+ attr_accessor :subcommand, :args, :options, :name, :files, :stylesheet
20
+ attr_reader :info
21
+
22
+ OUTPUT_TYPES = ['html', 'epub', 'mobi', 'pdf']
23
+ WORDSMITH_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
24
+
25
+ def initialize
26
+ @subcommand = nil
27
+ @args = []
28
+ @options = {}
29
+ @config = YAML::parse(File.open(local('.wordsmith'))).transform rescue {}
30
+ @name = File.basename(local('.'))
31
+ end
32
+
33
+ def info(message)
34
+ @info ||= []
35
+ @info << message
36
+ end
37
+
38
+ def local(file)
39
+ File.expand_path(File.join(Dir.pwd, file))
40
+ end
41
+
42
+ def base(file)
43
+ File.join(WORDSMITH_ROOT, file)
44
+ end
45
+ end
@@ -0,0 +1,81 @@
1
+ require 'subcommand'
2
+
3
+ class Wordsmith
4
+ include Subcommands
5
+
6
+ def info(message)
7
+ puts message
8
+ end
9
+
10
+ module CLI
11
+
12
+ def run
13
+ parse_options
14
+ if @subcommand && self.respond_to?(@subcommand)
15
+ begin
16
+ self.send @subcommand, @args
17
+ rescue Object => e
18
+ error e
19
+ end
20
+ else
21
+ help
22
+ end
23
+ end
24
+
25
+ def error(e)
26
+ puts 'Error: ' + e.to_s
27
+ end
28
+
29
+ def help
30
+ puts print_actions
31
+ end
32
+
33
+ def parse_options
34
+ @options = {}
35
+ global_options do |opts|
36
+ opts.banner = "Usage: #{$0} [options] [subcommand [options]]"
37
+ opts.description = "wordsmith helps you write books collectively with Git"
38
+ opts.separator ""
39
+ opts.separator "Global options are:"
40
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
41
+ @options[:verbose] = v
42
+ end
43
+ end
44
+
45
+ command :init, :new, :n do |opts|
46
+ opts.banner = "Usage: wordsmith new (directory)"
47
+ opts.description = "initialize a new book layout"
48
+ end
49
+
50
+ command :generate, :g do |opts|
51
+ opts.banner = "Usage: wordsmith generate [options]"
52
+ opts.description = "generate digital formats"
53
+ end
54
+
55
+ command :publish do |opts|
56
+ opts.banner = "Usage: wordsmith publish"
57
+ opts.description = "publish your book to github project page"
58
+ end
59
+
60
+ @subcommand = opt_parse
61
+ @args = ARGV
62
+ end
63
+
64
+ # DISPLAY HELPER FUNCTIONS #
65
+
66
+ def l(info, size)
67
+ clean(info)[0, size].ljust(size)
68
+ end
69
+
70
+ def r(info, size)
71
+ clean(info)[0, size].rjust(size)
72
+ end
73
+
74
+ def clean(info)
75
+ info.to_s.gsub("\n", ' ')
76
+ end
77
+
78
+ end
79
+
80
+ include CLI
81
+ end
@@ -0,0 +1,132 @@
1
+ class Wordsmith
2
+ module Generate
3
+
4
+ attr_reader :files, :output
5
+
6
+ # generate the new media
7
+ def generate(args = [])
8
+ @output = local(File.join('final', @name))
9
+
10
+ content_dir = local(File.join('content'))
11
+ @files = Dir.glob(content_dir + '/**/*.*').join(" \\\n")
12
+
13
+ if @files.empty?
14
+ raise "Exiting.. Nothing to generate in #{content_dir}.\nHave you run 'wordsmith new'?"
15
+ end
16
+
17
+ build_metadata_xml
18
+
19
+ @stylesheet = if @config["stylesheet"] && File.exists?(local(@config["stylesheet"]))
20
+ local(@config["stylesheet"])
21
+ end
22
+
23
+ Dir.mkdir(local('final')) unless File.exists?(local('final'))
24
+
25
+ formats = args.empty? ? OUTPUT_TYPES : args
26
+ formats.each do |format|
27
+ if respond_to?("to_#{format}")
28
+ if format == 'mobi'
29
+ out = to_mobi
30
+ else
31
+ out = run_command(send("to_#{format}"))
32
+ end
33
+ if $?.exitstatus == 0 && out == '' || $?.exitstatus == 1 && format == 'mobi'
34
+ if format == 'html'
35
+ info "Created #{@output}/index.html"
36
+ else
37
+ info "Created #{@output}.#{format}"
38
+ end
39
+ else
40
+ raise "#{format} generator failed"
41
+ end
42
+ else
43
+ raise "No generator found for #{format}"
44
+ end
45
+ end
46
+ end
47
+
48
+ def build_metadata_xml
49
+ metadata = local(File.join('metadata.xml'))
50
+ builder = Nokogiri::XML::Builder.new do |xml|
51
+ xml.metadata('xmlns:dc' => 'http://purl.org/dc/elements/1.1/') {
52
+ xml['dc'].title { xml.text @config["title"] }
53
+ xml['dc'].creator { xml.text @config["author"] }
54
+ xml['dc'].language { xml.text @config["language"] }
55
+ }
56
+ end
57
+ frg = Nokogiri::XML.fragment(builder.to_xml)
58
+ nodes = frg.search('.//metadata/*')
59
+ File.open(metadata, 'w') { |f| f.write(nodes.to_xml) }
60
+ end
61
+
62
+ def to_html
63
+ info "Generating html..."
64
+ html_dir = local(File.join('final', @name))
65
+ Dir.mkdir(html_dir) unless File.exists?(html_dir)
66
+ header = if File.exists?(local(File.join('layout', 'header.html')))
67
+ local(File.join('layout', 'header.html'))
68
+ end
69
+ footer = if File.exists?(local(File.join('layout', 'footer.html')))
70
+ local(File.join('layout', 'footer.html'))
71
+ end
72
+ `cp -r #{base(File.join('template', 'assets'))} #{html_dir}`
73
+ cmd = "pandoc -s -S --toc -o #{File.join(html_dir, 'index.html')} -t html"
74
+ puts @config['stylesheet']
75
+ cmd += " -c #{@config['stylesheet']}" if @stylesheet
76
+ cmd += " -B #{header}" if header
77
+ cmd += " -A #{footer}" if footer
78
+ cmd += " \\\n#{@files}"
79
+ end
80
+
81
+ def to_epub
82
+ info "Generating epub..."
83
+ metadata = if File.exists?(local(File.join('metadata.xml')))
84
+ local(File.join('metadata.xml'))
85
+ end
86
+ cover = if @config["cover"] && File.exists?(local(File.join(@config["cover"])))
87
+ local(File.join(@config["cover"]))
88
+ end
89
+ cmd = "pandoc -S -o #{@output}.epub -t epub"
90
+ cmd += " \\\n--epub-metadata=#{metadata}"
91
+ cmd += " \\\n--epub-cover-image=#{cover}" if cover
92
+ cmd += " \\\n--epub-stylesheet=#{@stylesheet}" if @stylesheet
93
+ cmd += " \\\n#{@files}"
94
+ end
95
+
96
+ def to_mobi
97
+ if File.exists?(@output + '.epub')
98
+ info "Generating mobi..."
99
+ Kindlegen.run("#{@output}.epub", "-o", "#{@name}.mobi")
100
+ else
101
+ info "Skipping .mobi (#{@name}.epub doesn't exist)"
102
+ end
103
+ end
104
+
105
+ def to_pdf
106
+ info "Generating pdf..."
107
+ engine = ''
108
+ [['pdftex', 'pdflatex'], ['xetex', 'xelatex'], 'lualatex'].each do |e|
109
+ if e.is_a? Array
110
+ cmd, name = e
111
+ else
112
+ cmd = name = e
113
+ end
114
+ if can_run?(cmd + ' -v')
115
+ engine = name
116
+ break
117
+ end
118
+ end
119
+ cmd = "pandoc -N --toc -o #{@output}.pdf #{@files}"
120
+ cmd += " --latex-engine=#{engine}" unless engine == ''
121
+ end
122
+
123
+ def run_command(cmd)
124
+ `#{cmd}`.strip
125
+ end
126
+
127
+ def can_run?(cmd)
128
+ `#{cmd} 2>&1`
129
+ $?.success?
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,27 @@
1
+ class Wordsmith
2
+ module Init
3
+
4
+ # start a new wordsmith directory with skeleton structure
5
+ def init(args = [])
6
+ name = Array(args).shift
7
+ raise "needs a directory name" unless name
8
+ raise "directory already exists" if File.exists?(name)
9
+
10
+ info "Creating wordsmith directory structure in #{name}"
11
+ template_dir = File.join(WORDSMITH_ROOT, 'template')
12
+ ign = Dir.glob(template_dir + '/.[a-z]*')
13
+ FileUtils.cp_r template_dir, name
14
+
15
+ # also copy files that start with .
16
+ FileUtils.cp_r ign, name
17
+ if Git.init(local(name))
18
+ info "Initialized empty Git repository in #{File.join(local(name), '.git')}"
19
+ @git = Git.open(local(name))
20
+ @git.add '.'
21
+ info "git add ."
22
+ @git.commit 'initial commit'
23
+ info "git commit -m 'initial commit'"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,62 @@
1
+ class Wordsmith
2
+ module Publish
3
+
4
+ # publish html book to github project page
5
+ def publish(args = [])
6
+ options = args.first
7
+
8
+ @git = Git.open(local('.'))
9
+
10
+ if options =~ /\.git/ && !@git.remotes.map(&:to_s).include?('origin')
11
+ @git.add_remote 'origin', options
12
+ info "Added remote origin #{options}"
13
+ elsif !@git.remotes.map(&:to_s).include?('origin')
14
+ raise "You must add a remote origin.\ne.g: wordsmith publish git@github.com:jassa/wordsmith-example.git"
15
+ end
16
+
17
+ if @git.current_branch != 'master'
18
+ begin
19
+ @git.checkout 'master'
20
+ info "Switched to branch 'master'"
21
+ rescue
22
+ raise "You must be in the 'master' branch to publish"
23
+ end
24
+ end
25
+
26
+ html_dir = File.join('final', @name)
27
+
28
+ unless File.exists?(File.join(html_dir, 'index.html'))
29
+ raise "Exiting.. Nothing to publish.\nHave you run 'wordsmith generate'?"
30
+ end
31
+
32
+ # http://pages.github.com/#project_pages
33
+ `git symbolic-ref HEAD refs/heads/gh-pages`
34
+ info "Switched to branch 'gh-pages'"
35
+ `rm .git/index`
36
+ `git clean -fdx`
37
+ info "Removed files"
38
+ `git checkout master #{html_dir}`
39
+ info "Copied files from 'master' #{html_dir}"
40
+ `mv #{html_dir}/* ./`
41
+ `rm -r final/`
42
+ `git add .`
43
+ `git rm -r final/*`
44
+ begin
45
+ @git.commit 'updating project page'
46
+ info "git commit -m 'updating project page'"
47
+ rescue Exception => e
48
+ if e.to_s =~ /nothing to commit/
49
+ raise 'Already up to date. Nothing to commit.'
50
+ else
51
+ raise e
52
+ end
53
+ end
54
+ @git.push 'origin', 'gh-pages'
55
+ info "git push origin gh-pages"
56
+ @git.checkout 'master'
57
+ rescue Exception => e
58
+ @git.checkout 'master' rescue nil
59
+ raise e
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ module Wordsmith
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ final/*.epub
2
+ final/*.mobi
3
+ final/*.pdf
4
+ .DS_STORE
@@ -0,0 +1,7 @@
1
+ ---
2
+ edition: 0.1
3
+ language: en
4
+ author: Your Name
5
+ title: Your Book Title
6
+ cover: assets/images/cover.jpg
7
+ stylesheet: assets/stylesheets/default.css
@@ -0,0 +1,3 @@
1
+ This book is written using the wordsmith gem, which can be found at:
2
+
3
+ http://github.com/tractical/wordsmith
@@ -0,0 +1,44 @@
1
+ html {
2
+ margin: 0;
3
+ padding: 40px;
4
+ background: #DFE1E5;
5
+ }
6
+ body {
7
+ margin: 0;
8
+ padding: 30px;
9
+ font-family: "Lucida Grande",verdana,arial,helvetica,sans-serif;
10
+ font-size: 14px;
11
+ color: #373737;
12
+ width: 800px;
13
+ background: rgba(255, 255, 255, 0.8);
14
+ -webkit-box-shadow: 2px 2px 6px rgba(0,0,0,.30);
15
+ -moz-box-shadow: 2px 2px 6px rgba(0,0,0,.30);
16
+ box-shadow: 2px 2px 6px rgba(0,0,0,.30);
17
+ border: 1px solid #C7C7C7;
18
+ -webkit-border-radius: 3px;
19
+ -moz-border-radius: 3px;
20
+ border-radius: 3px;
21
+
22
+ }
23
+ a {
24
+ color: #1A4882;
25
+ }
26
+ h1, h2, h3 {
27
+ margin: 30px 0 10px 0;
28
+ }
29
+ body {
30
+ display: inline-block;
31
+ }
32
+ div#TOC {
33
+ border: 1px solid #C7C7C7;
34
+ -webkit-border-radius: 3px;
35
+ -moz-border-radius: 3px;
36
+ border-radius: 3px;
37
+ }
38
+ div#cover {
39
+ text-align: center;
40
+ padding: 0 0 30px 0;
41
+ }
42
+ div#cover img {
43
+ max-height: 400px;
44
+ }
@@ -0,0 +1,12 @@
1
+ ## A Wordsmith's Manifesto
2
+
3
+ I eat, pray and love the written word. My every spare moment is spent reading,
4
+ writing, or listening to words that bring inspiration, comfort, wisdom and joy.
5
+
6
+ I am always poised to pen something down, especially when triggered by
7
+ a flash of insight, moved by a scene, or teased by an idea that refuses
8
+ to leave my head.
9
+
10
+ I carry a notepad and pen with me whenever I go for meetings, regardless of
11
+ the agenda. Capturing prodigious quantities of notes is always a good practice
12
+ for the inner journalist in me.
@@ -0,0 +1,10 @@
1
+ ## Read, Read, Read
2
+
3
+ I read, and read, and read. During bus rides to and from one destination
4
+ to another. Before I sleep at night. Whenever I'm waiting for
5
+ an activity to happen. You will always find a book in my bag - and yes,
6
+ I am a "bag man". Nothing triggers good writing better than good reads.
7
+
8
+ I'm a keen observer of my environment, the people surrounding me,
9
+ and the unique situations that I find myself in constantly. They provide fodder
10
+ to my literary outputs, planting the seeds of the narratives that chronicle my thoughts.
@@ -0,0 +1,20 @@
1
+ ## Pay Special Attention
2
+
3
+ I pay special attention to how every word is crafted and honed
4
+ to meet the page. I fuss over the flow and cadence of my sentences,
5
+ the expressions used, the points highlighted, and how they are
6
+ all assembled together. Especially the beginning and the end.
7
+
8
+ ### Being human
9
+
10
+ Being human, however, there will be "off" days when I can barely muster
11
+ a single virtuous word. I understand that good writing isn't as easy
12
+ as turning on a tap and letting it flow. Writer's block is
13
+ an inevitable occupational hazard that hits all of us.
14
+
15
+ ### Different strokes for different folks
16
+
17
+ I believe in different strokes for different folks. Write for your audiences
18
+ and write for the medium. There are distinct differences between an advertisement,
19
+ a press release, a blog post, an invitation email, a strategy paper, and
20
+ a diary documenting one's innermost thoughts.
@@ -0,0 +1,14 @@
1
+ ### There is a time and place for everything
2
+
3
+ I am always mindful of the contexts of my writing. There is a time and place
4
+ for everything under the Sun. Finding the right moment to tell the right story
5
+ to the right audience under the right conditions are crucial elements of writing
6
+ success. Anything else is a compromise.
7
+
8
+ ### I am a Wordsmith
9
+
10
+ I am always learning and always picking up new things.
11
+ While I trust in my own instincts, I am also painfully aware that I'm not omniscient.
12
+ I will make mistakes, but I will pick myself up again, brush off the dust, and carry on.
13
+
14
+ I am a wordsmith. Writing is my craft.
File without changes
@@ -0,0 +1,3 @@
1
+ <div id="cover">
2
+ <image src="assets/images/cover.jpg" />
3
+ </div>
@@ -0,0 +1,7 @@
1
+ require File.expand_path "../test_helper", __FILE__
2
+
3
+ context "wordsmith generate tests" do
4
+ setup do
5
+ @wordsmith = Wordsmith.new
6
+ end
7
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path "../test_helper", __FILE__
2
+
3
+ context "wordsmith init tests" do
4
+ setup do
5
+ @wordsmith = Wordsmith.new
6
+ end
7
+
8
+ test "can't init a wordsmith repo without a directory" do
9
+ in_temp_dir do
10
+ assert_raise RuntimeError do
11
+ @wordsmith.init
12
+ end
13
+ end
14
+ end
15
+
16
+ test "can't init a wordsmith repo for existing dir" do
17
+ in_temp_dir do
18
+ Dir.mkdir('w')
19
+ assert_raise RuntimeError do
20
+ @wordsmith.init('w')
21
+ end
22
+ end
23
+ end
24
+
25
+ test "can init a wordsmith repo" do
26
+ in_temp_dir do
27
+ @wordsmith.init('w')
28
+ files = Dir.glob('w/**/*', File::FNM_DOTMATCH)
29
+ assert files.include? "w/README.md"
30
+ assert files.include? "w/.wordsmith"
31
+ assert files.include? "w/.gitignore"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path "../test_helper", __FILE__
2
+
3
+ context "wordsmith publish tests" do
4
+ setup do
5
+ @wordsmith = Wordsmith.new
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ dir = File.dirname(File.expand_path(__FILE__))
2
+ $LOAD_PATH.unshift dir + '/../lib'
3
+ $TESTING = true
4
+
5
+ # Necessary to override stdlib: http://www.ruby-forum.com/topic/212974
6
+ require 'rubygems'
7
+ gem 'test-unit'
8
+ require 'test/unit'
9
+
10
+ require 'wordsmith'
11
+ require 'pp'
12
+ require 'tempfile'
13
+
14
+ ##
15
+ # test/spec/mini 3
16
+ # http://gist.github.com/25455
17
+ # chris@ozmm.org
18
+ #
19
+ def context(*args, &block)
20
+ return super unless (name = args.first) && block
21
+ require 'test/unit'
22
+ klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
23
+ def self.test(name, &block)
24
+ define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
25
+ end
26
+ def self.xtest(*args) end
27
+ def self.setup(&block) define_method(:setup, &block) end
28
+ def self.teardown(&block) define_method(:teardown, &block) end
29
+ end
30
+ (class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
31
+ klass.class_eval &block
32
+ end
33
+
34
+ def in_temp_dir
35
+ f = Tempfile.new('test')
36
+ p = f.path
37
+ f.unlink
38
+ Dir.mkdir(p)
39
+ Dir.chdir(p) do
40
+ yield
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ $LOAD_PATH.unshift 'lib'
3
+ require 'wordsmith/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "wordsmith"
7
+ s.version = Wordsmith::VERSION
8
+ s.authors = ["Amed Rodriguez", "Javier Saldana", "Rene Cienfuegos"]
9
+ s.email = ["amed@tractical.com", "javier@tractical.com", "renecienfuegos@gmail.com"]
10
+ s.homepage = "https://github.com/tractical/wordsmith"
11
+ s.summary = "E-books publisher."
12
+ s.description = "Create, collaborate and publish e-books -- easily."
13
+ s.has_rdoc = false
14
+
15
+ s.rubyforge_project = "wordsmith"
16
+
17
+ s.executables = %w( wordsmith )
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+
21
+ s.add_dependency('nokogiri')
22
+ s.add_dependency('kindlegen')
23
+ s.add_dependency("git")
24
+ s.add_development_dependency("rake")
25
+ s.add_development_dependency("test-unit")
26
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wordsmith
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Amed Rodriguez
9
+ - Javier Saldana
10
+ - Rene Cienfuegos
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-03-25 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: nokogiri
18
+ requirement: &70114637222780 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *70114637222780
27
+ - !ruby/object:Gem::Dependency
28
+ name: kindlegen
29
+ requirement: &70114637222320 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *70114637222320
38
+ - !ruby/object:Gem::Dependency
39
+ name: git
40
+ requirement: &70114637221800 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: *70114637221800
49
+ - !ruby/object:Gem::Dependency
50
+ name: rake
51
+ requirement: &70114637221360 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *70114637221360
60
+ - !ruby/object:Gem::Dependency
61
+ name: test-unit
62
+ requirement: &70114637220940 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *70114637220940
71
+ description: Create, collaborate and publish e-books -- easily.
72
+ email:
73
+ - amed@tractical.com
74
+ - javier@tractical.com
75
+ - renecienfuegos@gmail.com
76
+ executables:
77
+ - wordsmith
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - .gitignore
82
+ - Gemfile
83
+ - Gemfile.lock
84
+ - README.md
85
+ - Rakefile
86
+ - bin/wordsmith
87
+ - lib/subcommand.rb
88
+ - lib/wordsmith.rb
89
+ - lib/wordsmith/cli.rb
90
+ - lib/wordsmith/generate.rb
91
+ - lib/wordsmith/init.rb
92
+ - lib/wordsmith/publish.rb
93
+ - lib/wordsmith/version.rb
94
+ - template/.gitignore
95
+ - template/.wordsmith
96
+ - template/README.md
97
+ - template/assets/images/cover.jpg
98
+ - template/assets/stylesheets/default.css
99
+ - template/content/01_manifesto.md
100
+ - template/content/02_read_read_read.md
101
+ - template/content/03_pay_special_attention/01_intro.md
102
+ - template/content/03_pay_special_attention/02_conclusion.md
103
+ - template/layout/footer.html
104
+ - template/layout/header.html
105
+ - test/generate_test.rb
106
+ - test/init_test.rb
107
+ - test/publish_test.rb
108
+ - test/test_helper.rb
109
+ - wordsmith.gemspec
110
+ homepage: https://github.com/tractical/wordsmith
111
+ licenses: []
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project: wordsmith
130
+ rubygems_version: 1.8.17
131
+ signing_key:
132
+ specification_version: 3
133
+ summary: E-books publisher.
134
+ test_files:
135
+ - test/generate_test.rb
136
+ - test/init_test.rb
137
+ - test/publish_test.rb
138
+ - test/test_helper.rb