noe 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/LICENCE.txt +20 -0
- data/README.md +117 -0
- data/Rakefile +24 -0
- data/bin/noe +5 -0
- data/lib/noe.rb +39 -0
- data/lib/noe/commons.rb +18 -0
- data/lib/noe/config.rb +60 -0
- data/lib/noe/config.yaml +29 -0
- data/lib/noe/create.rb +77 -0
- data/lib/noe/go.rb +194 -0
- data/lib/noe/help.rb +26 -0
- data/lib/noe/install.rb +89 -0
- data/lib/noe/list.rb +39 -0
- data/lib/noe/main.rb +79 -0
- data/lib/noe/template.rb +144 -0
- data/noe.gemspec +36 -0
- data/spec/noe_spec.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/template/entry/relocate_spec.rb +41 -0
- data/spec/template/entry/rename_one_spec.rb +31 -0
- data/templates/ruby/CHANGELOG.md +3 -0
- data/templates/ruby/README.md +24 -0
- data/templates/ruby/noespec.yaml +32 -0
- data/templates/ruby/src/CHANGELOG.md +5 -0
- data/templates/ruby/src/LICENCE.txt +20 -0
- data/templates/ruby/src/README.md +3 -0
- data/templates/ruby/src/Rakefile +42 -0
- data/templates/ruby/src/__lower__.gemspec +31 -0
- data/templates/ruby/src/lib/__lower__.rb +6 -0
- data/templates/ruby/src/spec/__lower___spec.rb +8 -0
- data/templates/ruby/src/spec/spec_helper.rb +4 -0
- metadata +183 -0
data/lib/noe/help.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Noe
|
2
|
+
class Main
|
3
|
+
#
|
4
|
+
# Show help about a specific command
|
5
|
+
#
|
6
|
+
# SYNOPSIS
|
7
|
+
# #{program_name} #{command_name} COMMAND
|
8
|
+
#
|
9
|
+
class Help < Quickl::Command(__FILE__, __LINE__)
|
10
|
+
|
11
|
+
# Let NoSuchCommandError be passed to higher stage
|
12
|
+
no_react_to Quickl::NoSuchCommand
|
13
|
+
|
14
|
+
# Command execution
|
15
|
+
def execute(args)
|
16
|
+
if args.size != 1
|
17
|
+
puts super_command.help
|
18
|
+
else
|
19
|
+
cmd = has_command!(args.first, super_command)
|
20
|
+
puts cmd.help
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end # class Help
|
25
|
+
end # class Main
|
26
|
+
end # module Noe
|
data/lib/noe/install.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
module Noe
|
3
|
+
class Main
|
4
|
+
#
|
5
|
+
# Install default configuration and template.
|
6
|
+
#
|
7
|
+
# SYNOPSIS
|
8
|
+
# #{program_name} #{command_name} [--force] [FOLDER]
|
9
|
+
#
|
10
|
+
# OPTIONS
|
11
|
+
# #{summarized_options}
|
12
|
+
#
|
13
|
+
# DESCRIPTION
|
14
|
+
# This command will install Noe's default configuration under
|
15
|
+
# FOLDER/.noerc and a default ruby template under FOLDER/.noe.
|
16
|
+
# Unless stated otherwise, FOLDER is user's home.
|
17
|
+
#
|
18
|
+
# If FOLDER/.noerc already exists, the comand safely fails.
|
19
|
+
# Use --force to override existing configuration.
|
20
|
+
#
|
21
|
+
class Install < Quickl::Command(__FILE__, __LINE__)
|
22
|
+
include Noe::Commons
|
23
|
+
|
24
|
+
# Force mode ?
|
25
|
+
attr_reader :force
|
26
|
+
|
27
|
+
# Install options
|
28
|
+
options do |opt|
|
29
|
+
@force = false
|
30
|
+
opt.on('--force', '-f',
|
31
|
+
"Force overriding on all existing files"){
|
32
|
+
@force = true
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute(args)
|
37
|
+
if args.size > 1
|
38
|
+
raise Quickl::InvalidArgument, "Needless arguments: #{args[1..-1].join(' ')}"
|
39
|
+
end
|
40
|
+
folder = args.first || ENV['HOME']
|
41
|
+
|
42
|
+
noerc_file = File.join(folder, '.noerc')
|
43
|
+
noe_folder = File.join(folder, '.noe')
|
44
|
+
|
45
|
+
# generate .noerc
|
46
|
+
if File.exists?(noerc_file) and not(force)
|
47
|
+
raise Noe::Error, "#{noerc_file} already exists, use --force to override"
|
48
|
+
end
|
49
|
+
File.open(noerc_file, 'w') do |out|
|
50
|
+
def_config = File.join(File.dirname(__FILE__), 'config.yaml')
|
51
|
+
context = { :templates_dir => noe_folder}
|
52
|
+
out << WLang::file_instantiate(def_config, context, 'wlang/active-string')
|
53
|
+
end
|
54
|
+
|
55
|
+
# generate .noe folder
|
56
|
+
unless File.exists?(noe_folder)
|
57
|
+
FileUtils.mkdir(noe_folder)
|
58
|
+
end
|
59
|
+
|
60
|
+
# copy default templates
|
61
|
+
tdir = File.expand_path('../../../templates', __FILE__)
|
62
|
+
Dir[File.join(tdir, '*')].each do |tpl|
|
63
|
+
target = File.join(noe_folder, File.basename(tpl))
|
64
|
+
if File.exists?(target)
|
65
|
+
if force
|
66
|
+
FileUtils.rm_rf target
|
67
|
+
else
|
68
|
+
puts "#{target} already exists, use --force to override"
|
69
|
+
next
|
70
|
+
end
|
71
|
+
end
|
72
|
+
FileUtils.cp_r tpl, noe_folder
|
73
|
+
end
|
74
|
+
|
75
|
+
# say something!
|
76
|
+
puts "Noe successfully installed !"
|
77
|
+
puts
|
78
|
+
puts "What's next?"
|
79
|
+
puts " * cat #{noerc_file}"
|
80
|
+
puts " * ls -lA #{noe_folder}"
|
81
|
+
puts " * noe list"
|
82
|
+
puts " * noe create hello_world"
|
83
|
+
puts
|
84
|
+
puts "Thank you for using Noe (v#{Noe::VERSION}), enjoy!"
|
85
|
+
end
|
86
|
+
|
87
|
+
end # class Install
|
88
|
+
end # class Main
|
89
|
+
end # module Noe
|
data/lib/noe/list.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Noe
|
2
|
+
class Main
|
3
|
+
#
|
4
|
+
# List available templates.
|
5
|
+
#
|
6
|
+
# SYNOPSIS
|
7
|
+
# #{program_name} #{command_name}
|
8
|
+
#
|
9
|
+
# DESCRIPTION
|
10
|
+
# This command list project templates found in the templates folder.
|
11
|
+
# The later is checked as a side effect.
|
12
|
+
#
|
13
|
+
# TIP
|
14
|
+
# Run this command to know where templates are located!
|
15
|
+
#
|
16
|
+
class List < Quickl::Command(__FILE__, __LINE__)
|
17
|
+
include Noe::Commons
|
18
|
+
|
19
|
+
def execute(args)
|
20
|
+
unless args.empty?
|
21
|
+
raise Quickl::InvalidArgument, "Needless argument: #{args.join(', ')}"
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "Templates located in: #{templates_dir}"
|
25
|
+
Dir[File.join(templates_dir, '**')].collect do |tpl_dir|
|
26
|
+
begin
|
27
|
+
tpl = Template.new(tpl_dir)
|
28
|
+
puts " * %-#{25}s %s" % [ "#{tpl.name} (v#{tpl.version})" , tpl.description ]
|
29
|
+
tpl
|
30
|
+
rescue => ex
|
31
|
+
puts " * %-#{25}s %s" % [File.basename(tpl_dir), ex.message]
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end # class List
|
38
|
+
end # class Main
|
39
|
+
end # module Noe
|
data/lib/noe/main.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
module Noe
|
2
|
+
#
|
3
|
+
# Noe - A simple and extensible project generator
|
4
|
+
#
|
5
|
+
# SYNOPSIS
|
6
|
+
# #{program_name} [--version] [--help] COMMAND [cmd opts] ARGS...
|
7
|
+
#
|
8
|
+
# OPTIONS
|
9
|
+
# #{summarized_options}
|
10
|
+
#
|
11
|
+
# COMMANDS
|
12
|
+
# #{summarized_subcommands}
|
13
|
+
#
|
14
|
+
# DESCRIPTION
|
15
|
+
# Noe helps development via support for well-designed templates. See
|
16
|
+
# https://github.com/blambeau/noe for more information.
|
17
|
+
#
|
18
|
+
# See '#{program_name} help COMMAND' for more information on a specific command.
|
19
|
+
#
|
20
|
+
class Main < Quickl::Delegate(__FILE__, __LINE__)
|
21
|
+
|
22
|
+
# Configuration instance
|
23
|
+
attr_reader :config_file
|
24
|
+
|
25
|
+
# Show backtrace on error?
|
26
|
+
attr_reader :backtrace
|
27
|
+
|
28
|
+
# Returns Noe's configuration, loading it if required
|
29
|
+
def config
|
30
|
+
@config_file ||= find_config_file
|
31
|
+
Config.new(@config_file)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Finds the configuration file and loads automatically
|
35
|
+
def find_config_file
|
36
|
+
in_home = File.join(ENV['HOME'], '.noerc')
|
37
|
+
File.file?(in_home) ? in_home : nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# Install options
|
41
|
+
options do |opt|
|
42
|
+
# Set a specific configuration file to use
|
43
|
+
opt.on('--config=FILE',
|
44
|
+
'Use a specific config file (defaults to ~/.noerc)') do |f|
|
45
|
+
@config_file = valid_read_file!(f)
|
46
|
+
end
|
47
|
+
# Show backtrace on error
|
48
|
+
opt.on_tail("--backtrace",
|
49
|
+
"Show backtrace on error") do
|
50
|
+
@backtrace = true
|
51
|
+
end
|
52
|
+
# Show the help and exit
|
53
|
+
opt.on_tail("--help",
|
54
|
+
"Show help") do
|
55
|
+
raise Quickl::Help
|
56
|
+
end
|
57
|
+
# Show version and exit
|
58
|
+
opt.on_tail("--version",
|
59
|
+
"Show version") do
|
60
|
+
raise Quickl::Exit, "#{program_name} #{Noe::VERSION} (c) 2011, Bernard Lambeau"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Runs the command
|
65
|
+
def run(argv, requester = nil)
|
66
|
+
super
|
67
|
+
rescue Quickl::Error
|
68
|
+
raise
|
69
|
+
rescue Noe::Error => ex
|
70
|
+
puts "#{ex.class}: #{ex.message}"
|
71
|
+
puts ex.backtrace.join("\n") if backtrace
|
72
|
+
rescue StandardError => ex
|
73
|
+
puts "Oups, Noe encountered a serious problem! Please report if a bug."
|
74
|
+
puts "#{ex.class}: #{ex.message}"
|
75
|
+
puts ex.backtrace.join("\n")
|
76
|
+
end
|
77
|
+
|
78
|
+
end # class Main
|
79
|
+
end # module Noe
|
data/lib/noe/template.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
module Noe
|
2
|
+
class Template
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
# Main folder of the template
|
6
|
+
attr_reader :folder
|
7
|
+
|
8
|
+
# Loaded specification
|
9
|
+
attr_reader :spec
|
10
|
+
|
11
|
+
# Creates a template instance
|
12
|
+
def initialize(folder)
|
13
|
+
@folder = folder
|
14
|
+
__load
|
15
|
+
end
|
16
|
+
|
17
|
+
# Loads the template from its folder
|
18
|
+
def __load
|
19
|
+
if File.file?(spec_file) and File.readable?(spec_file)
|
20
|
+
@spec = YAML::load(File.read(spec_file))
|
21
|
+
else
|
22
|
+
raise Noe::Error, "Unable to find template: #{spec_file}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns path to the spec file
|
27
|
+
def spec_file
|
28
|
+
File.join(folder, "noespec.yaml")
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns template name
|
32
|
+
def name
|
33
|
+
File.basename(folder)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns template description
|
37
|
+
def description
|
38
|
+
spec['template-info']['description']
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns template version
|
42
|
+
def version
|
43
|
+
spec['template-info']['version']
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns path to the sources folder
|
47
|
+
def src_folder
|
48
|
+
File.join(folder, "src")
|
49
|
+
end
|
50
|
+
|
51
|
+
# Ignore some file?
|
52
|
+
def ignore?(file)
|
53
|
+
['.', '..'].include? File.basename(file)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns an entry for a given relative path
|
57
|
+
def entry(*paths)
|
58
|
+
Entry.new(self, paths.join(File::PATH_SEPARATOR))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Visit the template
|
62
|
+
def visit(entry = src_folder, &block)
|
63
|
+
if entry.is_a?(Entry)
|
64
|
+
block.call(entry)
|
65
|
+
else
|
66
|
+
entry = Entry.new(self, nil)
|
67
|
+
end
|
68
|
+
if entry.directory?
|
69
|
+
Dir.foreach(entry.realpath) do |child|
|
70
|
+
childentry = entry.child_entry(child)
|
71
|
+
unless ignore?(childentry.realpath)
|
72
|
+
visit(childentry, &block)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
alias :each :visit
|
78
|
+
|
79
|
+
private :__load
|
80
|
+
|
81
|
+
# Entry inside a template structure
|
82
|
+
class Entry
|
83
|
+
|
84
|
+
# Template where this entry is located
|
85
|
+
attr_reader :template
|
86
|
+
|
87
|
+
# Relative path of the entry inside the template
|
88
|
+
attr_reader :path
|
89
|
+
|
90
|
+
# Creates an entry instance
|
91
|
+
def initialize(template, path)
|
92
|
+
@template = template
|
93
|
+
@path = path
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns real absolute path of the entry
|
97
|
+
def realpath
|
98
|
+
path.nil? ? template.src_folder : File.join(template.src_folder, path)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns entry name
|
102
|
+
def name
|
103
|
+
File.basename(realpath)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Relocate the path according to variables
|
107
|
+
def relocate(variables)
|
108
|
+
path.split(File::PATH_SEPARATOR).
|
109
|
+
collect{|v| rename_one(variables, v)}.
|
110
|
+
join(File::PATH_SEPARATOR)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns the target name, according to some variables
|
114
|
+
def rename_one(variables, name = self.name)
|
115
|
+
if name =~ /__([a-z]+)__/
|
116
|
+
if x = variables[$1]
|
117
|
+
name.gsub(/__([a-z]+)__/, x)
|
118
|
+
else
|
119
|
+
raise Noe::Error, "Missing variable #{$1}"
|
120
|
+
end
|
121
|
+
else
|
122
|
+
name
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Is the entry a file?
|
127
|
+
def file?
|
128
|
+
File.file?(realpath)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Is the entry a directory?
|
132
|
+
def directory?
|
133
|
+
File.directory?(realpath)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Builds an child entry for a given name
|
137
|
+
def child_entry(name)
|
138
|
+
template.entry(path.nil? ? name : File.join(path, name))
|
139
|
+
end
|
140
|
+
|
141
|
+
end # class Entry
|
142
|
+
|
143
|
+
end # class Template
|
144
|
+
end # module Noe
|
data/noe.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path('../lib/noe', __FILE__)
|
2
|
+
spec = Gem::Specification.new do |s|
|
3
|
+
s.name = %q{noe}
|
4
|
+
s.version = Noe::VERSION.dup
|
5
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
6
|
+
|
7
|
+
s.author = %q{Bernard Lambeau}
|
8
|
+
s.email = %q{blambeau@gmail.com}
|
9
|
+
|
10
|
+
s.description = %q{A simple and extensible project generator}
|
11
|
+
s.summary = %q{Noe helps development by providing support for project templates and instantiation.}
|
12
|
+
|
13
|
+
s.require_paths = ["lib"]
|
14
|
+
s.bindir = "bin"
|
15
|
+
s.executables = ["noe"]
|
16
|
+
|
17
|
+
s.extra_rdoc_files = ["README.md", "CHANGELOG.md"]
|
18
|
+
|
19
|
+
s.files =
|
20
|
+
Dir['bin/**/*'] +
|
21
|
+
Dir['lib/**/*'] +
|
22
|
+
Dir['spec/**/*'] +
|
23
|
+
Dir['templates/**/*'] +
|
24
|
+
%w{ noe.gemspec Rakefile README.md CHANGELOG.md LICENCE.txt}
|
25
|
+
|
26
|
+
s.add_dependency('wlang', '>= 0.9.2')
|
27
|
+
s.add_dependency('quickl', '>= 0.2.0')
|
28
|
+
|
29
|
+
s.add_development_dependency('rake')
|
30
|
+
s.add_development_dependency('rspec', ">= 2.4.0")
|
31
|
+
s.add_development_dependency('yard', ">= 0.6.4")
|
32
|
+
s.add_development_dependency('bluecloth', ">= 0.6.4")
|
33
|
+
|
34
|
+
s.homepage = %q{http://github.com/blambeau/noe}
|
35
|
+
end
|
36
|
+
|
data/spec/noe_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
module Noe
|
3
|
+
describe "Template::Entry#relocate" do
|
4
|
+
|
5
|
+
let(:template){
|
6
|
+
Template.new(File.expand_path('../../../../templates/ruby', __FILE__))
|
7
|
+
}
|
8
|
+
let(:vars){
|
9
|
+
{"lower" => "project"}
|
10
|
+
}
|
11
|
+
subject{
|
12
|
+
entry.relocate(vars)
|
13
|
+
}
|
14
|
+
|
15
|
+
describe "when nothing has to change" do
|
16
|
+
let(:entry){ template.entry('.gitignore') }
|
17
|
+
it{ should == ".gitignore" }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "when exactly a replacement" do
|
21
|
+
let(:entry){ template.entry("__lower__") }
|
22
|
+
it { should == "project" }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "when a replacement inside something else" do
|
26
|
+
let(:entry){ template.entry("__lower___spec.rb") }
|
27
|
+
it { should == "project_spec.rb" }
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "when no replace and sub file" do
|
31
|
+
let(:entry){ template.entry("lib", "README.md") }
|
32
|
+
it { should == ["lib", "README.md"].join(File::PATH_SEPARATOR) }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "when no replace and sub file with replacement" do
|
36
|
+
let(:entry){ template.entry("lib", "__lower__.rb") }
|
37
|
+
it { should == ["lib", "project.rb"].join(File::PATH_SEPARATOR) }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end # module Noe
|