noe 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +27 -0
- data/Gemfile +2 -0
- data/LICENCE.md +22 -0
- data/README.md +111 -45
- data/Rakefile +18 -20
- data/bin/noe +1 -1
- data/lib/noe.rb +7 -21
- data/lib/noe/commons.rb +23 -0
- data/lib/noe/config.yaml +2 -2
- data/lib/noe/ext/array.rb +18 -0
- data/lib/noe/ext/hash.rb +48 -0
- data/lib/noe/go.rb +158 -40
- data/lib/noe/install.rb +13 -7
- data/lib/noe/list.rb +34 -10
- data/lib/noe/loader.rb +67 -0
- data/lib/noe/main.rb +15 -15
- data/lib/noe/prepare.rb +121 -0
- data/lib/noe/show_spec.rb +45 -0
- data/lib/noe/template.rb +84 -4
- data/noe.gemspec +186 -30
- data/spec/ext/hash/methodize_spec.rb +30 -0
- data/spec/noe_spec.rb +4 -0
- data/spec/spec_helper.rb +0 -2
- data/spec/template/entry/infer_wlang_dialect_spec.rb +31 -0
- data/tasks/gem.rake +44 -0
- data/tasks/spec_test.rake +61 -0
- data/tasks/unit_test.rake +56 -0
- data/tasks/yard.rake +36 -0
- data/templates/ruby/CHANGELOG.md +9 -1
- data/templates/ruby/README.md +47 -10
- data/templates/ruby/noespec.yaml +195 -26
- data/templates/ruby/short.yaml +33 -0
- data/templates/ruby/src/Gemfile +2 -0
- data/templates/ruby/src/LICENCE.md +22 -0
- data/templates/ruby/src/Manifest.txt +11 -0
- data/templates/ruby/src/README.md +6 -1
- data/templates/ruby/src/Rakefile +16 -36
- data/templates/ruby/src/__lower__.gemspec +178 -23
- data/templates/ruby/src/lib/__lower__.rb +5 -2
- data/templates/ruby/src/lib/__lower__/loader.rb +65 -0
- data/templates/ruby/src/spec/spec_helper.rb +0 -2
- data/templates/ruby/src/tasks/gem.rake +44 -0
- data/templates/ruby/src/tasks/spec_test.rake +61 -0
- data/templates/ruby/src/tasks/unit_test.rake +56 -0
- data/templates/ruby/src/tasks/yard.rake +36 -0
- metadata +349 -79
- data/LICENCE.txt +0 -20
- data/lib/noe/create.rb +0 -77
- data/templates/ruby/src/LICENCE.txt +0 -20
data/lib/noe/go.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Noe
|
2
2
|
class Main
|
3
3
|
#
|
4
|
-
# Instantiate a project template using a .noespec file
|
4
|
+
# Instantiate a project template using a .noespec file
|
5
5
|
#
|
6
6
|
# SYNOPSIS
|
7
|
-
# #{program_name} #{command_name} [
|
7
|
+
# #{program_name} #{command_name} [--dry-run] [--force|--interactive|--add-only|--safe-override] [SPEC_FILE]
|
8
8
|
#
|
9
9
|
# OPTIONS
|
10
10
|
# #{summarized_options}
|
@@ -14,18 +14,18 @@ module Noe
|
|
14
14
|
# given as first argument. If no spec file is specified, Noe expects
|
15
15
|
# one .noespec file to be present in the current directory and uses it.
|
16
16
|
#
|
17
|
-
# This command is generally used immediately after invoking '
|
17
|
+
# This command is generally used immediately after invoking 'prepare',
|
18
18
|
# on an almost empty directory. By default it safely fails if any file
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# options.
|
19
|
+
# would be overriden by the instantiation process. This safe behavior
|
20
|
+
# can be bypassed through the --force, --add-only, --interactive and
|
21
|
+
# --safe-override options.
|
22
22
|
#
|
23
23
|
# TYPICAL USAGE
|
24
24
|
#
|
25
25
|
# When a fresh new project is created, this command is typically used
|
26
|
-
#
|
26
|
+
# within the following scenario
|
27
27
|
#
|
28
|
-
# noe
|
28
|
+
# noe prepare --template=ruby hello_world
|
29
29
|
# cd hello_world
|
30
30
|
# edit hello_world.noespec
|
31
31
|
# noe go
|
@@ -41,17 +41,29 @@ module Noe
|
|
41
41
|
# rm README.md hello_world.gemspec
|
42
42
|
# noe go --add-only
|
43
43
|
#
|
44
|
+
# If you want to regenerate some files by controlling what will be
|
45
|
+
# overriden:
|
46
|
+
#
|
47
|
+
# noe go --interactive
|
48
|
+
#
|
49
|
+
# If you want to regenerate some files according to the template
|
50
|
+
# manifest:
|
51
|
+
#
|
52
|
+
# noe go --safe-override
|
53
|
+
#
|
44
54
|
class Go < Quickl::Command(__FILE__, __LINE__)
|
45
55
|
include Noe::Commons
|
46
56
|
|
47
|
-
# Dry-run mode ?
|
48
57
|
attr_reader :dry_run
|
49
58
|
|
50
|
-
# Force mode ?
|
51
59
|
attr_reader :force
|
60
|
+
alias :force? :force
|
52
61
|
|
53
|
-
# Only make additions ?
|
54
62
|
attr_reader :adds_only
|
63
|
+
alias :adds_only? :adds_only
|
64
|
+
|
65
|
+
attr_reader :safe_override
|
66
|
+
alias :safe_override? :safe_override
|
55
67
|
|
56
68
|
# Install options
|
57
69
|
options do |opt|
|
@@ -60,70 +72,171 @@ module Noe
|
|
60
72
|
"Say what would be done but don't do it"){
|
61
73
|
@dry_run = true
|
62
74
|
}
|
75
|
+
opt.separator ""
|
76
|
+
opt.separator "File overriding control: "
|
63
77
|
@force = false
|
64
78
|
opt.on('--force', '-f',
|
65
79
|
"Force overriding on all existing files"){
|
66
80
|
@force = true
|
67
81
|
}
|
82
|
+
@interactive = false
|
83
|
+
opt.on('--interactive', '-i',
|
84
|
+
"Request the user to take a decision"){
|
85
|
+
@interactive = true
|
86
|
+
@highline = HighLine.new
|
87
|
+
}
|
68
88
|
@adds_only = false
|
69
89
|
opt.on('--add-only', '-a',
|
70
90
|
"Only make additions, do not override any existing file"){
|
71
91
|
@adds_only = true
|
72
92
|
}
|
93
|
+
@safe_override = false
|
94
|
+
opt.on('--safe-override', '-s',
|
95
|
+
"Follow safe-override information provided by the manifest"){
|
96
|
+
@safe_override = true
|
97
|
+
}
|
98
|
+
Commons.add_common_options(opt)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Checks if the interactive mode is enabled. If yes, highline is prepared
|
102
|
+
# as a side effect.
|
103
|
+
def interactive?
|
104
|
+
if @interactive and not(@highline)
|
105
|
+
begin
|
106
|
+
require "highline"
|
107
|
+
@highline = HighLine.new
|
108
|
+
rescue LoadError
|
109
|
+
raise Quickl::Exit.new(1), "Highline is required for interactive mode, try 'gem install highline'"
|
110
|
+
end
|
111
|
+
else
|
112
|
+
@interactive
|
113
|
+
end
|
73
114
|
end
|
74
115
|
|
75
|
-
def
|
116
|
+
def say(what, color)
|
117
|
+
if @highline
|
118
|
+
@highline.say(@highline.color(what, color))
|
119
|
+
else
|
120
|
+
puts what
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def choose(&block)
|
125
|
+
@highline.choose(&block)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Checks if one is a file and the other a directory or the inverse
|
129
|
+
def kind_clash?(entry, relocated)
|
130
|
+
(entry.file? and File.directory?(relocated)) or
|
131
|
+
(entry.directory? and File.file?(relocated))
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_one_directory(entry, variables)
|
76
135
|
relocated = entry.relocate(variables)
|
77
136
|
todo = []
|
78
137
|
|
79
|
-
|
138
|
+
skipped = false
|
139
|
+
if File.exists?(relocated)
|
140
|
+
# file exists already exists, check what can be done!
|
141
|
+
if kind_clash?(entry, relocated)
|
142
|
+
if interactive?
|
143
|
+
say("File #{relocated} conflicts with directory to create", :red)
|
144
|
+
choose do |menu|
|
145
|
+
menu.prompt = "What do we do?"
|
146
|
+
menu.index = :letter
|
147
|
+
menu.choice(:abord) { raise Quickl::Exit.new(1), "Noe aborted!" }
|
148
|
+
menu.choice(:remove){ todo << Rm.new(entry, variables) }
|
149
|
+
end
|
150
|
+
elsif force?
|
151
|
+
todo << Rm.new(entry, variables)
|
152
|
+
else
|
153
|
+
raise Quickl::Exit.new(2), "Noe aborted: file #{relocated} already exists.\n"\
|
154
|
+
"Use --force to override or --interactive for more options."
|
155
|
+
end
|
156
|
+
else
|
157
|
+
# file exists and is already a folder; we simply do nothing
|
158
|
+
# because there is no dangerous action here
|
159
|
+
skipped = true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Create the directory unless it has been explicitely skipped
|
164
|
+
unless skipped
|
165
|
+
todo << MkDir.new(entry, variables)
|
166
|
+
end
|
167
|
+
|
168
|
+
todo
|
169
|
+
end
|
170
|
+
|
171
|
+
def build_one_file(entry, variables)
|
172
|
+
relocated = entry.relocate(variables)
|
173
|
+
todo = []
|
174
|
+
|
175
|
+
skipped = false
|
80
176
|
if File.exists?(relocated)
|
81
|
-
|
82
|
-
|
177
|
+
# name clash, the file exists
|
178
|
+
if adds_only?
|
179
|
+
# file exists and we are only allowed to add new things
|
180
|
+
# we just do nothing
|
181
|
+
skipped = true
|
182
|
+
elsif interactive? and kind_clash?(entry, relocated)
|
183
|
+
say("Directory #{relocated} conflicts with file to create", :red)
|
184
|
+
choose do |menu|
|
185
|
+
menu.prompt = "What do we do?"
|
186
|
+
menu.index = :letter
|
187
|
+
menu.choice(:abord) { raise Quickl::Exit.new(1), "Noe aborted!" }
|
188
|
+
menu.choice(:remove){ todo << Rm.new(entry, variables) }
|
189
|
+
menu.choice(:skip) { skipped = true }
|
190
|
+
end
|
191
|
+
elsif interactive?
|
192
|
+
say("File #{relocated} already exists", :red)
|
193
|
+
choose do |menu|
|
194
|
+
would = entry.safe_override? ? :override : :skip
|
195
|
+
menu.prompt = "What do we do? (safe-override would #{would})"
|
196
|
+
menu.index = :letter
|
197
|
+
menu.choice(:abord) { raise Quickl::Exit.new(1), "Noe aborted!" }
|
198
|
+
menu.choice(:override){ todo << Rm.new(entry, variables) }
|
199
|
+
menu.choice(:skip) { skipped = true }
|
200
|
+
end
|
201
|
+
elsif safe_override?
|
202
|
+
if entry.safe_override?
|
83
203
|
todo << Rm.new(entry, variables)
|
204
|
+
else
|
205
|
+
skipped = true
|
84
206
|
end
|
85
|
-
elsif
|
86
|
-
|
207
|
+
elsif force?
|
208
|
+
todo << Rm.new(entry, variables)
|
87
209
|
else
|
88
|
-
raise
|
89
|
-
|
210
|
+
raise Quickl::Exit.new(2), "Noe aborted: file #{relocated} already exists.\n"\
|
211
|
+
"Use --force to override or --interactive for more options."
|
90
212
|
end
|
91
213
|
end
|
92
214
|
|
93
|
-
#
|
94
|
-
|
95
|
-
todo << MkDir.new(entry, variables)
|
96
|
-
|
97
|
-
# Create files
|
98
|
-
elsif entry.file?
|
215
|
+
# Add file instantiation unless skipped
|
216
|
+
unless skipped
|
99
217
|
todo << FileInstantiate.new(entry, variables)
|
100
|
-
|
101
218
|
end
|
219
|
+
|
102
220
|
todo
|
103
221
|
end
|
104
222
|
|
105
223
|
def execute(args)
|
106
224
|
raise Quickl::Help if args.size > 1
|
107
225
|
|
108
|
-
# Find spec file
|
109
|
-
spec_file = if args.size == 1
|
110
|
-
valid_read_file!(args.first)
|
111
|
-
else
|
112
|
-
spec_files = Dir['*.noespec']
|
113
|
-
if spec_files.size > 1
|
114
|
-
raise Noe::Error, "Ambiguous request, multiple specs: #{spec_files.join(', ')}"
|
115
|
-
end
|
116
|
-
spec_files.first
|
117
|
-
end
|
118
|
-
|
119
226
|
# Load spec now
|
227
|
+
spec_file = find_noespec_file(args)
|
120
228
|
spec = YAML::load(File.read(spec_file))
|
121
229
|
template = template(spec['template-info']['name'])
|
122
|
-
|
230
|
+
template.merge_spec(spec)
|
231
|
+
variables = template.variables
|
123
232
|
|
124
233
|
# Build what has to be done
|
125
234
|
commands = template.collect{|entry|
|
126
|
-
|
235
|
+
if entry.file?
|
236
|
+
build_one_file(entry, variables)
|
237
|
+
elsif entry.directory?
|
238
|
+
build_one_directory(entry, variables)
|
239
|
+
end
|
127
240
|
}.flatten
|
128
241
|
|
129
242
|
# let's go now
|
@@ -144,6 +257,10 @@ module Noe
|
|
144
257
|
@entry, @variables = entry, variables
|
145
258
|
end
|
146
259
|
|
260
|
+
def template
|
261
|
+
entry.template
|
262
|
+
end
|
263
|
+
|
147
264
|
def relocated
|
148
265
|
entry.relocate(variables)
|
149
266
|
end
|
@@ -178,7 +295,8 @@ module Noe
|
|
178
295
|
|
179
296
|
def run
|
180
297
|
File.open(relocated, 'w') do |out|
|
181
|
-
dialect =
|
298
|
+
dialect = entry.wlang_dialect
|
299
|
+
variables.methodize!
|
182
300
|
out << WLang::file_instantiate(entry.realpath, variables, dialect)
|
183
301
|
end
|
184
302
|
end
|
data/lib/noe/install.rb
CHANGED
@@ -2,7 +2,7 @@ require 'fileutils'
|
|
2
2
|
module Noe
|
3
3
|
class Main
|
4
4
|
#
|
5
|
-
# Install default configuration and template.
|
5
|
+
# Install default configuration and template (not required).
|
6
6
|
#
|
7
7
|
# SYNOPSIS
|
8
8
|
# #{program_name} #{command_name} [--force] [FOLDER]
|
@@ -11,12 +11,17 @@ module Noe
|
|
11
11
|
# #{summarized_options}
|
12
12
|
#
|
13
13
|
# DESCRIPTION
|
14
|
-
# This command will install Noe's default configuration under
|
15
|
-
#
|
16
|
-
#
|
14
|
+
# This command will install Noe's default configuration under FOLDER/.noerc
|
15
|
+
# and a default ruby template under FOLDER/.noe. Unless stated otherwise,
|
16
|
+
# FOLDER is user's home.
|
17
17
|
#
|
18
|
-
# If FOLDER/.noerc already exists, the comand safely fails.
|
19
|
-
#
|
18
|
+
# If FOLDER/.noerc already exists, the comand safely fails. Use --force to
|
19
|
+
# override existing configuration.
|
20
|
+
#
|
21
|
+
# TIP
|
22
|
+
# Installing default templates and configuration is NOT required. Noe uses
|
23
|
+
# their internal representation by default. Use 'noe install' only if you
|
24
|
+
# plan to create your own templates or want to tune default ones.
|
20
25
|
#
|
21
26
|
class Install < Quickl::Command(__FILE__, __LINE__)
|
22
27
|
include Noe::Commons
|
@@ -31,6 +36,7 @@ module Noe
|
|
31
36
|
"Force overriding on all existing files"){
|
32
37
|
@force = true
|
33
38
|
}
|
39
|
+
Commons.add_common_options(opt)
|
34
40
|
end
|
35
41
|
|
36
42
|
def execute(args)
|
@@ -79,7 +85,7 @@ module Noe
|
|
79
85
|
puts " * cat #{noerc_file}"
|
80
86
|
puts " * ls -lA #{noe_folder}"
|
81
87
|
puts " * noe list"
|
82
|
-
puts " * noe
|
88
|
+
puts " * noe prepare hello_world"
|
83
89
|
puts
|
84
90
|
puts "Thank you for using Noe (v#{Noe::VERSION}), enjoy!"
|
85
91
|
end
|
data/lib/noe/list.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module Noe
|
2
2
|
class Main
|
3
3
|
#
|
4
|
-
# List available templates
|
4
|
+
# List available templates
|
5
5
|
#
|
6
6
|
# SYNOPSIS
|
7
7
|
# #{program_name} #{command_name}
|
8
8
|
#
|
9
9
|
# DESCRIPTION
|
10
|
-
# This command
|
10
|
+
# This command lists project templates found in the templates folder.
|
11
11
|
# The later is checked as a side effect.
|
12
12
|
#
|
13
13
|
# TIP
|
@@ -16,20 +16,44 @@ module Noe
|
|
16
16
|
class List < Quickl::Command(__FILE__, __LINE__)
|
17
17
|
include Noe::Commons
|
18
18
|
|
19
|
+
options do |opt|
|
20
|
+
Commons.add_common_options(opt)
|
21
|
+
end
|
22
|
+
|
23
|
+
def max(i,j)
|
24
|
+
i > j ? i : j
|
25
|
+
end
|
26
|
+
|
19
27
|
def execute(args)
|
20
28
|
unless args.empty?
|
21
29
|
raise Quickl::InvalidArgument, "Needless argument: #{args.join(', ')}"
|
22
30
|
end
|
23
31
|
|
24
|
-
|
25
|
-
|
32
|
+
tpls = Dir[File.join(templates_dir, '**')].collect{|tpldir| Template.new(tpldir)}
|
33
|
+
columns = [:name, :version, :layouts, :summary]
|
34
|
+
data = [ columns ] + tpls.collect{|tpl|
|
26
35
|
begin
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
36
|
+
columns.collect{|col|
|
37
|
+
if col == :layouts
|
38
|
+
(tpl.send(col) - ["noespec"]).to_a.join(',')
|
39
|
+
else
|
40
|
+
tpl.send(col).to_s.strip
|
41
|
+
end
|
42
|
+
}
|
43
|
+
rescue Exception => ex
|
44
|
+
[ tpl.name, "", "", "Template error: #{ex.message}" ]
|
45
|
+
end
|
46
|
+
}
|
47
|
+
lengths = data.inject([0,0,0,0]){|memo,columns|
|
48
|
+
(0..3).collect{|i| max(memo[i], columns[i].to_s.length)}
|
49
|
+
}
|
50
|
+
puts "Templates available in #{templates_dir}"
|
51
|
+
data.each_with_index do |line,i|
|
52
|
+
current = (config.default == line[0])
|
53
|
+
puts (current ? " -> " : " ") +
|
54
|
+
"%-#{lengths[0]}s %-#{lengths[1]}s %-#{lengths[2]}s %-#{lengths[3]}s" % line
|
55
|
+
if i==0
|
56
|
+
puts "-"*lengths.inject(0){|memo,i| memo+i+3}
|
33
57
|
end
|
34
58
|
end
|
35
59
|
end
|
data/lib/noe/loader.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Noe
|
2
|
+
#
|
3
|
+
# This module provides tools to load stdlib and gem dependencies.
|
4
|
+
#
|
5
|
+
module Loader
|
6
|
+
|
7
|
+
#
|
8
|
+
# This method allows requiring dependencies with some flexibility.
|
9
|
+
#
|
10
|
+
# Implemented algorithm makes greedy choices about the environment:
|
11
|
+
# 1. It first attempts a simple <code>Kernel.require(name)</code> before
|
12
|
+
# anything else (even bypassing version requirement)
|
13
|
+
# 2. If step 1 fails with a LoadError then it falls back requiring the
|
14
|
+
# gem with specified version (defaults to >= 0) and retries step 1.
|
15
|
+
# 3. If step 2 fails with a NameError, 'rubygems' are required and step
|
16
|
+
# 2 is retried.
|
17
|
+
# 4. If step 3. fails, the initial LoadError is reraised.
|
18
|
+
#
|
19
|
+
# Doing so ensures flexibility for the users of the library by not making
|
20
|
+
# wrong assumptions about their environment. Testing the library is also
|
21
|
+
# made easier, as illustrated in the examples below. Please note that this
|
22
|
+
# method is useful to load external dependencies of your code only, not
|
23
|
+
# .rb files of your own library.
|
24
|
+
#
|
25
|
+
# Examples:
|
26
|
+
#
|
27
|
+
# # Require something from the standard library
|
28
|
+
# Noe::Loader.require('fileutils')
|
29
|
+
#
|
30
|
+
# # Require a gem without specifing any particular version
|
31
|
+
# Noe::Loader.require('highline')
|
32
|
+
#
|
33
|
+
# # Require a gem, specifing a particular version
|
34
|
+
# Noe::Loader.require('foo', "~> 1.6")
|
35
|
+
#
|
36
|
+
# # Twist the load path to use version of foo you've recently
|
37
|
+
# # forked (bypass the version requirement)
|
38
|
+
# $LOAD_PATH.unshift ... # or ruby -I...
|
39
|
+
# Noe::Loader.require('highline', "~> 1.6")
|
40
|
+
#
|
41
|
+
# Learn more about this pattern:
|
42
|
+
# - http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices
|
43
|
+
# - https://gist.github.com/54177
|
44
|
+
#
|
45
|
+
def require(name, version = nil)
|
46
|
+
Kernel.require name.to_s
|
47
|
+
rescue LoadError
|
48
|
+
begin
|
49
|
+
gem name.to_s, version || ">= 0"
|
50
|
+
rescue NameError
|
51
|
+
if $VERBOSE
|
52
|
+
Kernel.warn "#{__FILE__}:#{__LINE__}: warning: requiring rubygems myself, "\
|
53
|
+
" you should use 'ruby -rubygems' instead. "\
|
54
|
+
"See https://gist.github.com/54177"
|
55
|
+
end
|
56
|
+
require "rubygems"
|
57
|
+
gem name.to_s, version || ">= 0"
|
58
|
+
end
|
59
|
+
Kernel.require name.to_s
|
60
|
+
end
|
61
|
+
module_function :require
|
62
|
+
|
63
|
+
end # module Loader
|
64
|
+
end # module Noe
|
65
|
+
Noe::Loader.require("wlang", "~> 0.10.0")
|
66
|
+
Noe::Loader.require("quickl", "~> 0.2.0")
|
67
|
+
Noe::Loader.require("highline", "~> 1.6.0")
|