latex-project-template 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ # latex-project-template
2
+
3
+ Management of latex template files.
4
+ latex-project-template creates a latex project directory from a template
5
+ in ~/.latex\_project\_template.
6
+ The directory is git repository and includes Rakefile to compile
7
+ a latex file with latexmk.
8
+
9
+ - <http://rubygems.org/gems/latex-project-template>
10
+ - <https://github.com/ytaka/latex-project-template>
11
+
12
+ ## Requirements
13
+
14
+ latex-project-template uses latexmk to compile latex files and
15
+ the project directory is managed by git.
16
+
17
+ - [latexmk](http://www.phys.psu.edu/~collins/software/latexmk-jcc/)
18
+ - [Git](http://git-scm.com/)
19
+ - some LaTeX environment
20
+
21
+ latex-project-template depends on the following gems.
22
+
23
+ - git
24
+ - filename
25
+ - user_config
26
+
27
+ ## Install
28
+
29
+ We can install by rubygems.
30
+
31
+ gem install latex-project-template
32
+
33
+ ## Special notations of template file names
34
+
35
+ \_\_IMPORT\_\_
36
+ :In \_\_IMPORT\_\_ we write list of files to import from other template.
37
+ \_\_PROJECT\_\_
38
+ :Replace \_\_PROJECT\_\_ by name of project.
39
+ \_\_DOT\_\_
40
+ :Replace \_\_DOT\_\_ by '.'.
41
+ \_\_IGNORE\_\_
42
+ :Files including \_\_IGNORE\_\_ are ignored.
43
+
44
+ ## Contributing to latex-project-template
45
+
46
+ - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
47
+ - Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
48
+ - Fork the project
49
+ - Start a feature/bugfix branch
50
+ - Commit and push until you are happy with your contribution
51
+ - Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
52
+ - Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
53
+
54
+ ## Copyright
55
+
56
+ Copyright (c) 2011 Takayuki YAMAGUCHI. See LICENSE.txt for
57
+ further details.
58
+
@@ -0,0 +1,45 @@
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 = "latex-project-template"
16
+ gem.homepage = "http://github.com/ytaka/latex-project-template"
17
+ gem.license = "GPLv3"
18
+ gem.summary = "LaTeX Project Template"
19
+ gem.description = "Create LaTeX project with git from template, which uses latexmk."
20
+ gem.email = "d@ytak.info"
21
+ gem.authors = ["Takayuki YAMAGUCHI"]
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 'git', '>= 1.2.5'
25
+ gem.add_runtime_dependency 'filename', '>= 0.1.0'
26
+ gem.add_runtime_dependency 'user_config', '>= 0.0.1'
27
+ gem.add_development_dependency 'rspec', '>= 2.5.0'
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rspec/core'
32
+ require 'rspec/core/rake_task'
33
+ RSpec::Core::RakeTask.new(:spec) do |spec|
34
+ spec.pattern = FileList['spec/**/*_spec.rb']
35
+ end
36
+
37
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ task :default => :spec
43
+
44
+ require 'yard'
45
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/latex_project_template'
4
+ require 'optparse'
5
+
6
+ help_message =<<HELP
7
+ Usage: #{File.basename(__FILE__)} project [template]
8
+ #{File.basename(__FILE__)} --list
9
+ #{File.basename(__FILE__)} --delete template
10
+ HELP
11
+
12
+ options = {
13
+ :mode => :create
14
+ }
15
+
16
+ begin
17
+ OptionParser.new(help_message) do |opt|
18
+ opt.on("--list", "List templates.") do |v|
19
+ options[:mode] = :list
20
+ end
21
+ opt.on("--delete", "Delete template.") do |v|
22
+ options[:mode] = :delete
23
+ end
24
+ opt.on("--init", "Initialize configuration directory.") do |v|
25
+ options[:mode] = :init
26
+ end
27
+ opt.on("--home DIR", "Set the directory having '.latex_project_template'.") do |v|
28
+ options[:home] = v
29
+ end
30
+ opt.parse!(ARGV)
31
+ end
32
+ rescue OptionParser::InvalidOption
33
+ $stderr.print <<MES
34
+ error: Invalid Option
35
+ #{help_message}
36
+ MES
37
+ exit(2)
38
+ rescue OptionParser::InvalidArgument
39
+ $stderr.print <<MES
40
+ error: Invalid Argument
41
+ #{help_message}
42
+ MES
43
+ exit(2)
44
+ end
45
+
46
+ case options[:mode]
47
+ when :init
48
+ LaTeXProjectTemplate::Configuration.create_new_config(options[:home])
49
+ when :list
50
+ LaTeXProjectTemplate::Configuration.new(options[:home]).list_template.each do |template|
51
+ $stdout.puts template
52
+ end
53
+ when :delete
54
+ if template = ARGV[0]
55
+ LaTeXProjectTemplate::Configuration.new(options[:home]).delete_template(template)
56
+ else
57
+ $stderr.puts "Please set template name."
58
+ $stderr.puts help_message
59
+ exit(1)
60
+ end
61
+ when :create
62
+ dir = ARGV[0]
63
+ template = ARGV[1] || 'default'
64
+ if dir
65
+ LaTeXProjectTemplate.new(dir, template, options[:home]).create(:io => $stdout)
66
+ else
67
+ exit(1)
68
+ end
69
+ end
@@ -0,0 +1,193 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+ require 'git'
4
+ require 'filename'
5
+ require 'user_config'
6
+
7
+ class LaTeXProjectTemplate
8
+ DEFAULT_CONFIG = ".latex_project_template"
9
+
10
+ class LPTConfig < UserConfig
11
+ end
12
+
13
+ class Configuration
14
+ def self.create_new_config(home_path = nil)
15
+ config = LPTConfig.new(DEFAULT_CONFIG, :home => home_path)
16
+ dir = config.directory
17
+ Dir.glob("#{File.expand_path(File.join(File.dirname(__FILE__), '../template/'))}/*").each do |d|
18
+ FileUtils.cp_r(d, dir)
19
+ end
20
+ end
21
+
22
+ def initialize(home_path)
23
+ @config = UserConfig.new(DEFAULT_CONFIG, :home => home_path)
24
+ end
25
+
26
+ def config_directory
27
+ @config.directory
28
+ end
29
+
30
+ def list_template
31
+ @config.list_in_directory('.')
32
+ end
33
+
34
+ def template_exist?(template)
35
+ if path = @config.exist?(template)
36
+ return LaTeXProjectTemplate::Directory.new(path)
37
+ end
38
+ false
39
+ end
40
+
41
+ def template_file(template, name)
42
+ unless path = @config.template_exist?(File.join(template, name))
43
+ path = @config.template_exist?(File.join('default', name))
44
+ end
45
+ path
46
+ end
47
+
48
+ def delete_template(template)
49
+ if String === template && template.size > 0
50
+ @config.delete(template)
51
+ else
52
+ raise ArgumentError, "Invalid template name to delete: #{template.inspect}"
53
+ end
54
+ end
55
+ end
56
+
57
+ class Directory
58
+ attr_reader :path, :name
59
+
60
+ def initialize(path)
61
+ @path = File.expand_path(path)
62
+ @name = File.basename(@path)
63
+ end
64
+
65
+ def create_directory_if_needed(target_dir)
66
+ if File.exist?(target_dir)
67
+ unless File.directory?(target_dir)
68
+ raise "Can not create directory: #{target_dir}"
69
+ end
70
+ else
71
+ File.mkdir_p(target_dir)
72
+ end
73
+ end
74
+ private :create_directory_if_needed
75
+
76
+ def target_path(project_name, target_dir, target_name)
77
+ target_name = target_name.gsub(/__DOT__/, '.')
78
+ target_name = target_name.gsub(/__PROJECT__/, project_name)
79
+ File.join(target_dir, target_name.sub(/^#{Regexp.escape(@path)}/, ''))
80
+ end
81
+ private :target_path
82
+
83
+ # Create file of which name is created by removing '.erb' from name of original file
84
+ def create_erb_template(erb_file, erb_obj, project_name, target_dir)
85
+ product_path = target_path(project_name, target_dir, erb_file.sub(/\.erb$/, ''))
86
+ erb_obj.instance_exec(erb_file, product_path) do |path, out|
87
+ erb = ERB.new(File.read(path))
88
+ open(out, 'w') do |f|
89
+ f.print erb.result(binding)
90
+ end
91
+ end
92
+ product_path
93
+ end
94
+ private :create_erb_template
95
+
96
+ IGNORE_FILE_REGEXP = /(\/__IMPORT__$|\/__IGNORE__|~$)/
97
+
98
+ def copy_to_directory(target_dir, erb_binding_obj, files = nil)
99
+ create_directory_if_needed(target_dir)
100
+ if files
101
+ file_list = files.map do |file_path|
102
+ fullpath = File.join(@path, file_path)
103
+ if File.exist?(fullpath)
104
+ fullpath
105
+ else
106
+ nil
107
+ end
108
+ end.compact
109
+ else
110
+ file_list = Dir.glob(File.join(@path, '**', '*')).sort
111
+ end
112
+ created_files = []
113
+ file_list.each do |file|
114
+ next if IGNORE_FILE_REGEXP =~ file
115
+ create_directory_if_needed(File.dirname(file))
116
+ unless File.directory?(file)
117
+ case file
118
+ when /\.erb$/
119
+ created = create_erb_template(file, erb_binding_obj, erb_binding_obj.project_name, target_dir)
120
+ else
121
+ created = target_path(erb_binding_obj.project_name, target_dir, file)
122
+ FileUtils.cp(file, created)
123
+ end
124
+ created_files << created
125
+ end
126
+ end
127
+ created_files
128
+ end
129
+
130
+ def files_to_import
131
+ import = Hash.new { |h, k| h[k] = [] }
132
+ import_list_path = File.join(@path, '__IMPORT__')
133
+ if File.exist?(import_list_path)
134
+ File.read(import_list_path).each_line do |l|
135
+ l.strip!
136
+ if n = l.index('/')
137
+ k = l.slice!(0...n)
138
+ import[k] << l[1..-1]
139
+ end
140
+ end
141
+ end
142
+ import
143
+ end
144
+ end
145
+
146
+ class ErbObject
147
+ attr_reader :project_name
148
+
149
+ def initialize(project_name)
150
+ @project_name = project_name
151
+ end
152
+ end
153
+
154
+ def initialize(dir, template, home_directory = nil)
155
+ @config = LaTeXProjectTemplate::Configuration.new(home_directory)
156
+ @target_dir = File.expand_path(dir)
157
+ unless @template = @config.template_exist?(template)
158
+ raise ArgumentError, "Can not find template: #{template}"
159
+ end
160
+ if File.exist?(@target_dir)
161
+ raise ArgumentError, "File #{@target_dir} exists."
162
+ end
163
+ @project_name = File.basename(dir).sub(/\/$/, '')
164
+ end
165
+
166
+ def create_files
167
+ erb_obj = LaTeXProjectTemplate::ErbObject.new(@project_name)
168
+ created_files = @template.copy_to_directory(@target_dir, erb_obj)
169
+ @template.files_to_import.each do |name, files|
170
+ if template_to_import = @config.template_exist?(name)
171
+ created_files.concat(template_to_import.copy_to_directory(@target_dir, erb_obj, files))
172
+ end
173
+ end
174
+ created_files.sort!
175
+ end
176
+ private :create_files
177
+
178
+ def create(opts = {})
179
+ FileUtils.mkdir_p(@target_dir)
180
+ git = Git.init(@target_dir)
181
+ files = create_files
182
+ git.add
183
+ git.commit("Copy template '#{@template.name}'.")
184
+ if io = opts[:io]
185
+ files.map! do |path|
186
+ path.sub!(@target_dir, '')
187
+ path.sub!(/^\//, '')
188
+ end
189
+ io.puts "Create the following files from template '#{@template.name}' and commit to git."
190
+ io.puts files.join("\n")
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,222 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'filename'
4
+
5
+ class LaTeXProjectTemplate
6
+ class Cleaning
7
+ include Rake::DSL if defined?(Rake::DSL)
8
+
9
+ attr_reader :temporary, :product, :pattern
10
+
11
+ DEFAULT_PATTERN = ["**/*~", "**/*.bak"]
12
+
13
+ def initialize
14
+ @pattern = Rake::FileList.new(DEFAULT_PATTERN)
15
+ @pattern.clear_exclude
16
+ end
17
+
18
+ def clean_files(list)
19
+ list.each do |fn|
20
+ begin
21
+ rm_r fn
22
+ rescue
23
+ nil
24
+ end
25
+ end
26
+ end
27
+ private :clean_files
28
+
29
+ def clean
30
+ clean_files(@pattern)
31
+ end
32
+ end
33
+
34
+ class Latexmk
35
+ COMMAND_TO_PRODUCE_FILE = [:dvi, :ps, :pdf, :pdfdvi, :pdfps]
36
+
37
+ include Rake::DSL if defined?(Rake::DSL)
38
+
39
+ attr_accessor :path
40
+
41
+ def initialize()
42
+ @path = 'latexmk'
43
+ @command = {}
44
+
45
+ COMMAND_TO_PRODUCE_FILE.each do |type|
46
+ set(type) do |target|
47
+ "#{@path} -#{type.to_s} #{target}"
48
+ end
49
+ end
50
+ set(:clean) do |target|
51
+ "#{@path} -c"
52
+ end
53
+ set(:distclean) do |target|
54
+ "#{@path} -C"
55
+ end
56
+ end
57
+
58
+ def set(sym, &block)
59
+ @command[sym] = block
60
+ end
61
+
62
+ def command(sym, target)
63
+ @command[sym] && @command[sym].call(target)
64
+ end
65
+
66
+ def execute_command(sym, target)
67
+ if c = command(sym, target)
68
+ sh(c)
69
+ end
70
+ end
71
+
72
+ (COMMAND_TO_PRODUCE_FILE + [:clean, :distclean]).each do |sym|
73
+ define_method(sym) do |target|
74
+ execute_command(sym, target)
75
+ end
76
+ end
77
+ end
78
+
79
+ class Task < Rake::TaskLib
80
+ attr_accessor :latexmk, :clean, :default
81
+
82
+ def initialize(target, &block)
83
+ @target = target
84
+ @latexmk = Latexmk.new
85
+ @clean = Cleaning.new
86
+ @default = :pdf
87
+ yield(self) if block_given?
88
+ define_task
89
+ end
90
+
91
+ def extension_from_command_type(type)
92
+ if /^pdf/ =~ type.to_s
93
+ ".pdf"
94
+ else
95
+ ".#{type.to_s}"
96
+ end
97
+ end
98
+ private :extension_from_command_type
99
+
100
+ def snapshot_of_current(type)
101
+ path = FileName.create(@target, :add => :prohibit, :extension => extension_from_command_type(type))
102
+ snapshot_path = FileName.create("snapshot", File.basename(path),
103
+ :type => :time, :directory => :parent, :position => :middle,
104
+ :delimiter => '', :add => :always, :format => "%Y%m%d_%H%M%S")
105
+ begin
106
+ Rake::Task[type].execute
107
+ rescue
108
+ $stderr.puts "Can not compile"
109
+ end
110
+ if File.exist?(path)
111
+ move(path, snapshot_path)
112
+ return snapshot_path
113
+ end
114
+ nil
115
+ end
116
+ private :snapshot_of_current
117
+
118
+ def commit_date(commit)
119
+ log_data = `git log --date=iso -1 #{commit}`
120
+ if $? == 0
121
+ require 'time'
122
+ l = log_data.split("\n").find { |s| /^Date:.*$/ =~ s }
123
+ Time.parse(l.sub("Date:", ''))
124
+ else
125
+ nil
126
+ end
127
+ end
128
+ private :commit_date
129
+
130
+ def extract_source(commit)
131
+ source_directory = FileName.create('src', :type => :time, :directory => :self, :add => :always)
132
+ c = "git archive --format=tar #{commit} | tar -C #{source_directory} -xf -"
133
+ system(c)
134
+ source_directory
135
+ end
136
+ private :extract_source
137
+
138
+ def snapshot_of_commit(type, commit)
139
+ if date = commit_date(commit)
140
+ source_directory = extract_source(commit)
141
+ path = FileName.create(source_directory, File.basename(@target), :add => :prohibit, :extension => extension_from_command_type(type))
142
+ path_base = File.basename(path).sub(/\.#{type}$/, "_#{date.strftime("%Y%m%d_%H%M%S")}.#{type}")
143
+ snapshot_path = FileName.create("snapshot", path_base, :directory => :parent, :position => :middle)
144
+ cd source_directory
145
+ begin
146
+ sh "rake #{type}"
147
+ rescue
148
+ $stderr.puts "Can not compile. Please edit files in #{source_directory}."
149
+ end
150
+ if File.exist?(path)
151
+ move(path, snapshot_path)
152
+ cd '..'
153
+ rm_r source_directory
154
+ return snapshot_path
155
+ end
156
+ else
157
+ $stderr.puts "The commit '#{commit}' does not exist."
158
+ end
159
+ nil
160
+ end
161
+ private :snapshot_of_commit
162
+
163
+ def define_task
164
+ desc "Default task"
165
+ task :default => @default
166
+
167
+ desc "Clean up temporary files."
168
+ task :clean do |t|
169
+ @latexmk.clean(@target)
170
+ @clean.clean
171
+ end
172
+
173
+ desc "Clean up all files."
174
+ task :distclean do |t|
175
+ @latexmk.distclean(@target)
176
+ @clean.clean
177
+ end
178
+
179
+ Latexmk::COMMAND_TO_PRODUCE_FILE.each do |type|
180
+ desc "Create #{type.to_s}."
181
+ task type => [@target] do |t|
182
+ @latexmk.__send__(type, @target)
183
+ end
184
+ end
185
+
186
+ desc "Create snapshot file."
187
+ task :snapshot, [:type,:commit] do |t, args|
188
+ type = args.type && args.type.size > 0 ? args.type.intern : :pdf
189
+ unless Latexmk::COMMAND_TO_PRODUCE_FILE.include?(type)
190
+ raise "Invalid option to produce file: #{type}."
191
+ end
192
+ if commit = args.commit
193
+ path = snapshot_of_commit(type, commit)
194
+ else
195
+ path = snapshot_of_current(type)
196
+ end
197
+ if path
198
+ $stdout.puts "Save to #{path}"
199
+ else
200
+ $stdout.puts "We can not create a file."
201
+ end
202
+ end
203
+
204
+ desc "Create source of particular commit."
205
+ task :source, [:commit] do |t, args|
206
+ if commit = args.commit
207
+ if date = commit_date(commit)
208
+ if source_directory = extract_source(commit)
209
+ $stdout.puts "Save to #{source_directory}"
210
+ else
211
+ $stdout.puts "We can not create directory of source files."
212
+ end
213
+ else
214
+ $stderr.puts "The commit '#{commit}' does not exist."
215
+ end
216
+ else
217
+ $stderr.puts "Please set commit."
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end