noe 1.0.0 → 1.1.0
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.
- 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")
|