noe 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG.md +27 -0
  2. data/Gemfile +2 -0
  3. data/LICENCE.md +22 -0
  4. data/README.md +111 -45
  5. data/Rakefile +18 -20
  6. data/bin/noe +1 -1
  7. data/lib/noe.rb +7 -21
  8. data/lib/noe/commons.rb +23 -0
  9. data/lib/noe/config.yaml +2 -2
  10. data/lib/noe/ext/array.rb +18 -0
  11. data/lib/noe/ext/hash.rb +48 -0
  12. data/lib/noe/go.rb +158 -40
  13. data/lib/noe/install.rb +13 -7
  14. data/lib/noe/list.rb +34 -10
  15. data/lib/noe/loader.rb +67 -0
  16. data/lib/noe/main.rb +15 -15
  17. data/lib/noe/prepare.rb +121 -0
  18. data/lib/noe/show_spec.rb +45 -0
  19. data/lib/noe/template.rb +84 -4
  20. data/noe.gemspec +186 -30
  21. data/spec/ext/hash/methodize_spec.rb +30 -0
  22. data/spec/noe_spec.rb +4 -0
  23. data/spec/spec_helper.rb +0 -2
  24. data/spec/template/entry/infer_wlang_dialect_spec.rb +31 -0
  25. data/tasks/gem.rake +44 -0
  26. data/tasks/spec_test.rake +61 -0
  27. data/tasks/unit_test.rake +56 -0
  28. data/tasks/yard.rake +36 -0
  29. data/templates/ruby/CHANGELOG.md +9 -1
  30. data/templates/ruby/README.md +47 -10
  31. data/templates/ruby/noespec.yaml +195 -26
  32. data/templates/ruby/short.yaml +33 -0
  33. data/templates/ruby/src/Gemfile +2 -0
  34. data/templates/ruby/src/LICENCE.md +22 -0
  35. data/templates/ruby/src/Manifest.txt +11 -0
  36. data/templates/ruby/src/README.md +6 -1
  37. data/templates/ruby/src/Rakefile +16 -36
  38. data/templates/ruby/src/__lower__.gemspec +178 -23
  39. data/templates/ruby/src/lib/__lower__.rb +5 -2
  40. data/templates/ruby/src/lib/__lower__/loader.rb +65 -0
  41. data/templates/ruby/src/spec/spec_helper.rb +0 -2
  42. data/templates/ruby/src/tasks/gem.rake +44 -0
  43. data/templates/ruby/src/tasks/spec_test.rake +61 -0
  44. data/templates/ruby/src/tasks/unit_test.rake +56 -0
  45. data/templates/ruby/src/tasks/yard.rake +36 -0
  46. metadata +349 -79
  47. data/LICENCE.txt +0 -20
  48. data/lib/noe/create.rb +0 -77
  49. data/templates/ruby/src/LICENCE.txt +0 -20
@@ -12,8 +12,11 @@ module Noe
12
12
  # #{summarized_subcommands}
13
13
  #
14
14
  # DESCRIPTION
15
- # Noe helps development via support for well-designed templates. See
16
- # https://github.com/blambeau/noe for more information.
15
+ # Noe is a tool that generates projects from predefined skeletons (aka project
16
+ # templates). Skeletons are designed for building specific products (a ruby
17
+ # library, a static or dynamic web site, ...). Noe instantiates them and helps
18
+ # you maintaining your product via meta-information provided by a .noespec yaml
19
+ # file. See https://github.com/blambeau/noe for more information.
17
20
  #
18
21
  # See '#{program_name} help COMMAND' for more information on a specific command.
19
22
  #
@@ -27,8 +30,10 @@ module Noe
27
30
 
28
31
  # Returns Noe's configuration, loading it if required
29
32
  def config
30
- @config_file ||= find_config_file
31
- Config.new(@config_file)
33
+ @config ||= begin
34
+ @config_file ||= find_config_file
35
+ Config.new(@config_file)
36
+ end
32
37
  end
33
38
 
34
39
  # Finds the configuration file and loads automatically
@@ -49,22 +54,17 @@ module Noe
49
54
  "Show backtrace on error") do
50
55
  @backtrace = true
51
56
  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
57
+ Commons.add_common_options(opt)
62
58
  end
63
59
 
64
60
  # Runs the command
65
61
  def run(argv, requester = nil)
66
62
  super
67
- rescue Quickl::Error
63
+ rescue WLang::Error => ex
64
+ puts "#{ex.class}: #{ex.message}"
65
+ back = ex.wlang_backtrace || ex.backtrace
66
+ puts back.join("\n")
67
+ rescue Quickl::Error => ex
68
68
  raise
69
69
  rescue Noe::Error => ex
70
70
  puts "#{ex.class}: #{ex.message}"
@@ -0,0 +1,121 @@
1
+ require 'fileutils'
2
+ module Noe
3
+ class Main
4
+ #
5
+ # Prepare the generation of a new project
6
+ #
7
+ # SYNOPSIS
8
+ # #{program_name} #{command_name} [options] [PROJECT_NAME]
9
+ #
10
+ # OPTIONS
11
+ # #{summarized_options}
12
+ #
13
+ # DESCRIPTION
14
+ # This command prepares the generation of a fresh new project according to
15
+ # its first argument:
16
+ #
17
+ # - With a project name given as first argument, a new folder is created and
18
+ # a .noespec file is generated in it for the selected template (see below).
19
+ #
20
+ # - Without project name, Noe assumes that you want the current folder to
21
+ # be upgraded to follow the template conventions. In that case, a fresh
22
+ # new .noespec file is simply generated in the current folder.
23
+ #
24
+ # The template specified in ~/.noerc under :default is used by default.
25
+ # Use --template=xxx to override this.
26
+ #
27
+ # After creation, you'll have to edit the generated .noespec file then run
28
+ # 'noe go'.
29
+ #
30
+ # TYPICAL USAGE
31
+ #
32
+ # To create a fresh new project for a given template (ruby here), play the
33
+ # following scenario:
34
+ #
35
+ # # start creation of a fresh new ruby project
36
+ # noe prepare [--template=ruby] [--layout=short] foo
37
+ # cd foo
38
+ #
39
+ # # edit the configuration
40
+ # edit foo.noespec
41
+ #
42
+ # # launch template generation
43
+ # noe go
44
+ #
45
+ # To upgrade an existing project to follow a template (ruby here), the following
46
+ # scenario is worth considering:
47
+ #
48
+ # # generate a .noespec file in the current folder (let assume 'foo')
49
+ # noe prepare [--template=ruby] [--layout=short]
50
+ #
51
+ # # edit the configuration
52
+ # edit foo.noespec
53
+ #
54
+ # # launch template generation in interactive mode
55
+ # noe go --interactive
56
+ #
57
+ class Prepare < Quickl::Command(__FILE__, __LINE__)
58
+ include Noe::Commons
59
+
60
+ # Force mode?
61
+ attr_reader :force
62
+
63
+ # Install options
64
+ options do |opt|
65
+ opt.on('--template=TEMPLATE_NAME',
66
+ "Set the template to use (try 'noe list' to see what exists)") do |name|
67
+ config.default = name
68
+ end
69
+ @layout = "noespec"
70
+ opt.on('--layout=LAYOUT',
71
+ "Set the specification layout to use (idem)") do |l|
72
+ @layout = l
73
+ end
74
+ @force = false
75
+ opt.on('--force', '-f',
76
+ "Force overriding of existing .noespec file"){
77
+ @force = true
78
+ }
79
+ Commons.add_common_options(opt)
80
+ end
81
+
82
+ def generate_noespec_file(where)
83
+ if File.exists?(where) and not(force)
84
+ raise Noe::Error, "File #{where} already exists, remove it first or set --force."
85
+ else
86
+ tpl = template
87
+ File.open(where, 'w') do |out|
88
+ context = {'template_name' => tpl.name}
89
+ out << WLang::file_instantiate(tpl.spec_layout_file(@layout), context, "wlang/active-text")
90
+ end
91
+ end
92
+ where
93
+ end
94
+
95
+ def execute(args)
96
+ pname, where = nil, nil
97
+ case args.size
98
+ when 0
99
+ pname = File.basename(File.expand_path('.'))
100
+ where = generate_noespec_file("#{pname}.noespec")
101
+ when 1
102
+ pname = args.first
103
+ FileUtils.mkdir(pname) unless File.exists?(pname)
104
+ where = generate_noespec_file(File.join(pname, "#{pname}.noespec"))
105
+ else
106
+ raise Quickl::Help unless args.size > 1
107
+ end
108
+
109
+ # what's next
110
+ puts "Project successfully started !"
111
+ puts
112
+ puts "What's next?"
113
+ puts " * edit #{where}"
114
+ puts " * noe go"
115
+ puts
116
+ puts "Thank you for using Noe (v#{Noe::VERSION}), enjoy!"
117
+ end
118
+
119
+ end # class Create
120
+ end # class Main
121
+ end # module Noe
@@ -0,0 +1,45 @@
1
+ module Noe
2
+ class Main
3
+ #
4
+ # Show the actual noe specification that would be used by 'noe go'
5
+ #
6
+ # SYNOPSIS
7
+ # #{program_name} #{command_name} [SPEC_FILE]
8
+ #
9
+ # OPTIONS
10
+ # #{summarized_options}
11
+ #
12
+ # DESCRIPTION
13
+ # This command merges the .noespec file given as first parameter (or found in
14
+ # the current folder) with the noespec.yaml file of the template and prints
15
+ # the result on the standard output.
16
+ #
17
+ # When 'noe go' is invoked, the actual specification it works with is the
18
+ # merging of what you specify in your .noespec file with default values
19
+ # provided by the template specification itself. In other words, your .noespec
20
+ # file simply overrides the default values provided in the template itself.
21
+ #
22
+ # Therefore, you can always keep your .noespec files simple by not specifying
23
+ # entries for which the default value is ok. However, making so could lead you
24
+ # to forget about some template options and this command is useful is such
25
+ # situations.
26
+ #
27
+ class ShowSpec < Quickl::Command(__FILE__, __LINE__)
28
+ include Noe::Commons
29
+
30
+ options do |opt|
31
+ Commons.add_common_options(opt)
32
+ end
33
+
34
+ def execute(args)
35
+ raise Quickl::Help if args.size > 1
36
+ spec_file = find_noespec_file(args)
37
+ spec = YAML::load(File.read(spec_file))
38
+ template = template(spec['template-info']['name'])
39
+ template.merge_spec(spec)
40
+ puts template.to_yaml
41
+ end
42
+
43
+ end # class ShowSpec
44
+ end # class Main
45
+ end # module Noe
@@ -23,9 +23,30 @@ module Noe
23
23
  end
24
24
  end
25
25
 
26
- # Returns path to the spec file
27
- def spec_file
28
- File.join(folder, "noespec.yaml")
26
+ def spec_layout_file(layout = 'noespec')
27
+ file = File.join(folder, "#{layout}.yaml")
28
+ if File.exists?(file)
29
+ file
30
+ else
31
+ puts "On #{file}"
32
+ raise Noe::Error, "Unknown specification layout: #{layout}, try 'noe list'"
33
+ end
34
+ end
35
+ alias :spec_file :spec_layout_file
36
+
37
+ # Returns an array with available layout names
38
+ def layouts
39
+ Dir[File.join(folder, '*.yaml')].collect{|f| File.basename(f, '.yaml')}
40
+ end
41
+
42
+ # Merges another spec file inside this template
43
+ def merge_spec_file(file)
44
+ merge_spec YAML::load(File.read(spec_file))
45
+ end
46
+
47
+ # Merges template spec with another spec given from a Hash
48
+ def merge_spec(hash)
49
+ @spec = @spec.noe_merge(hash)
29
50
  end
30
51
 
31
52
  # Returns template name
@@ -33,6 +54,11 @@ module Noe
33
54
  File.basename(folder)
34
55
  end
35
56
 
57
+ # Returns template summary
58
+ def summary
59
+ spec['template-info']['summary'] || description
60
+ end
61
+
36
62
  # Returns template description
37
63
  def description
38
64
  spec['template-info']['description']
@@ -43,6 +69,15 @@ module Noe
43
69
  spec['template-info']['version']
44
70
  end
45
71
 
72
+ # Returns the template variables entry
73
+ def variables
74
+ spec['variables']
75
+ end
76
+
77
+ def main_wlang_dialect
78
+ spec['template-info']['main-wlang-dialect']
79
+ end
80
+
46
81
  # Returns path to the sources folder
47
82
  def src_folder
48
83
  File.join(folder, "src")
@@ -58,6 +93,15 @@ module Noe
58
93
  Entry.new(self, paths.join(File::PATH_SEPARATOR))
59
94
  end
60
95
 
96
+ # Returns manifest Hash for a given entry
97
+ def manifest_for(entry)
98
+ manifest = spec['template-info']['manifest'] || {}
99
+ manifest[entry.path] || {
100
+ 'description' => "No description for #{entry.path}",
101
+ 'safe-override' => false
102
+ }
103
+ end
104
+
61
105
  # Visit the template
62
106
  def visit(entry = src_folder, &block)
63
107
  if entry.is_a?(Entry)
@@ -76,6 +120,11 @@ module Noe
76
120
  end
77
121
  alias :each :visit
78
122
 
123
+ # Delegated to spec
124
+ def to_yaml(*args)
125
+ spec.to_yaml(*args)
126
+ end
127
+
79
128
  private :__load
80
129
 
81
130
  # Entry inside a template structure
@@ -104,7 +153,7 @@ module Noe
104
153
  end
105
154
 
106
155
  # Relocate the path according to variables
107
- def relocate(variables)
156
+ def relocate(variables = template.variables)
108
157
  path.split(File::PATH_SEPARATOR).
109
158
  collect{|v| rename_one(variables, v)}.
110
159
  join(File::PATH_SEPARATOR)
@@ -137,6 +186,37 @@ module Noe
137
186
  def child_entry(name)
138
187
  template.entry(path.nil? ? name : File.join(path, name))
139
188
  end
189
+
190
+ # Returns the hash with the manifest for this entry
191
+ def manifest
192
+ template.manifest_for(self)
193
+ end
194
+
195
+ # Returns wlang dialect to use
196
+ def wlang_dialect
197
+ @wlang_dialect ||= begin
198
+ default = template.main_wlang_dialect
199
+ manifest['wlang-dialect'] || self.class.infer_wlang_dialect(relocate, default)
200
+ end
201
+ end
202
+
203
+ # Infers the wlang dialect to use for the entry
204
+ def self.infer_wlang_dialect(uri, default = nil)
205
+ res = case d = WLang::infer_dialect(uri)
206
+ when nil
207
+ nil
208
+ when /^wlang/
209
+ d
210
+ else
211
+ WLang::dialect("wlang/#{d}").qualified_name
212
+ end
213
+ res ? res : (default || 'wlang/active-text')
214
+ end
215
+
216
+ # Is this entry safe to override
217
+ def safe_override?
218
+ manifest['safe-override']
219
+ end
140
220
 
141
221
  end # class Entry
142
222
 
@@ -1,36 +1,192 @@
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')
1
+ # We require your library, mainly to have access to the VERSION number.
2
+ # Feel free to set $version manually.
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
4
+ require "noe"
5
+ $version = Noe::VERSION.dup
6
6
 
7
- s.author = %q{Bernard Lambeau}
8
- s.email = %q{blambeau@gmail.com}
7
+ #
8
+ # This is your Gem specification. Default values are provided so that your library
9
+ # should be correctly packaged given what you have described in the .noespec file.
10
+ #
11
+ Gem::Specification.new do |s|
12
+
13
+ ################################################################### ABOUT YOUR GEM
14
+
15
+ # Gem name (required)
16
+ s.name = "noe"
17
+
18
+ # Gem version (required)
19
+ s.version = $version
20
+
21
+ # A short summary of this gem
22
+ #
23
+ # This is displayed in `gem list -d`.
24
+ s.summary = "Noe is a tool that generates projects from predefined skeletons (aka project/application templates). Skeletons are designed for building specific products (a ruby library, a static or dynamic web site, ...). Noe instantiates them and helps you maintaining your product via meta-information provided by a .noespec yaml file."
9
25
 
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.}
26
+ # A long description of this gem (required)
27
+ #
28
+ # The description should be more detailed than the summary. For example,
29
+ # you might wish to copy the entire README into the description.
30
+ s.description = File.read(File.expand_path('../README.md', __FILE__))
31
+
32
+ # The URL of this gem home page (optional)
33
+ s.homepage = "http://github.com/blambeau/noe"
12
34
 
35
+ # Gem publication date (required but auto)
36
+ #
37
+ # Today is automatically used by default, uncomment only if
38
+ # you know what you do!
39
+ #
40
+ # s.date = Time.now.strftime('%Y-%m-%d')
41
+
42
+ # The license(s) for the library. Each license must be a short name, no
43
+ # more than 64 characters.
44
+ #
45
+ # s.licences = %w{}
46
+
47
+ # The rubyforge project this gem lives under (optional)
48
+ #
49
+ # s.rubyforge_project = nil
50
+
51
+ ################################################################### ABOUT THE AUTHORS
52
+
53
+ # The list of author names who wrote this gem.
54
+ #
55
+ # If you are providing multiple authors and multiple emails they should be
56
+ # in the same order.
57
+ #
58
+ s.authors = ["Bernard Lambeau"]
59
+
60
+ # Contact emails for this gem
61
+ #
62
+ # If you are providing multiple authors and multiple emails they should be
63
+ # in the same order.
64
+ #
65
+ # NOTE: Somewhat strangly this attribute is always singular!
66
+ # Don't replace by s.emails = ...
67
+ s.email = ["blambeau@gmail.com"]
68
+
69
+ ################################################################### PATHS, FILES, BINARIES
70
+
71
+ # Paths in the gem to add to $LOAD_PATH when this gem is
72
+ # activated (required).
73
+ #
74
+ # The default 'lib' is typically sufficient.
13
75
  s.require_paths = ["lib"]
76
+
77
+ # Files included in this gem.
78
+ #
79
+ # By default, we take all files included in the Manifest.txt file on root
80
+ # of the project. Entries of the manifest are interpreted as Dir[...]
81
+ # patterns so that lazy people may use wilcards like lib/**/*
82
+ #
83
+ here = File.dirname(__FILE__)
84
+ s.files = File.readlines(File.join(here, 'Manifest.txt')).
85
+ inject([]){|files, pattern|
86
+ files + Dir[File.join(here, pattern.strip)]
87
+ }
88
+
89
+ # Test files included in this gem.
90
+ #
91
+ s.test_files = Dir["test/**/*"] + Dir["spec/**/*"]
92
+
93
+ # The path in the gem for executable scripts (optional)
94
+ #
14
95
  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
96
 
97
+ # Executables included in the gem.
98
+ #
99
+ s.executables = (Dir["bin/*"]).collect{|f| File.basename(f)}
100
+
101
+ ################################################################### REQUIREMENTS & INSTALL
102
+ # Remember the gem version requirements operators and schemes:
103
+ # = Equals version
104
+ # != Not equal to version
105
+ # > Greater than version
106
+ # < Less than version
107
+ # >= Greater than or equal to
108
+ # <= Less than or equal to
109
+ # ~> Approximately greater than
110
+ #
111
+ # Don't forget to have a look at http://lmgtfy.com/?q=Ruby+Versioning+Policies
112
+ # for setting your gem version.
113
+ #
114
+ # For your requirements to other gems, remember that
115
+ # ">= 2.2.0" (optimistic: specify minimal version)
116
+ # ">= 2.2.0", "< 3.0" (pessimistic: not greater than the next major)
117
+ # "~> 2.2" (shortcut for ">= 2.2.0", "< 3.0")
118
+ # "~> 2.2.0" (shortcut for ">= 2.2.0", "< 2.3.0")
119
+ #
120
+
121
+ #
122
+ # One call to add_dependency('gem_name', 'gem version requirement') for each
123
+ # runtime dependency. These gems will be installed with your gem.
124
+ # One call to add_development_dependency('gem_name', 'gem version requirement')
125
+ # for each development dependency. These gems are required for developers
126
+ #
127
+ s.add_development_dependency("rake", "~> 0.8.7")
128
+ s.add_development_dependency("bundler", "~> 1.0")
129
+ s.add_development_dependency("rspec", "~> 2.4.0")
130
+ s.add_development_dependency("yard", "~> 0.6.4")
131
+ s.add_development_dependency("bluecloth", "~> 2.0.9")
132
+ s.add_dependency("wlang", "~> 0.10.0")
133
+ s.add_dependency("quickl", "~> 0.2.0")
134
+ s.add_dependency("highline", "~> 1.6.0")
135
+
136
+ # The version of ruby required by this gem
137
+ #
138
+ # Uncomment and set this if your gem requires specific ruby versions.
139
+ #
140
+ # s.required_ruby_version = ">= 0"
141
+
142
+ # The RubyGems version required by this gem
143
+ #
144
+ # s.required_rubygems_version = ">= 0"
145
+
146
+ # The platform this gem runs on. See Gem::Platform for details.
147
+ #
148
+ # s.platform = nil
149
+
150
+ # Extensions to build when installing the gem.
151
+ #
152
+ # Valid types of extensions are extconf.rb files, configure scripts
153
+ # and rakefiles or mkrf_conf files.
154
+ #
155
+ s.extensions = []
156
+
157
+ # External (to RubyGems) requirements that must be met for this gem to work.
158
+ # It’s simply information for the user.
159
+ #
160
+ s.requirements = nil
161
+
162
+ # A message that gets displayed after the gem is installed
163
+ #
164
+ # Uncomment and set this if you want to say something to the user
165
+ # after gem installation
166
+ #
167
+ s.post_install_message = "Noe successfully installed!\n\nWhat's next?\n - 'noe help install' for configuration and default templates\n - 'noe prepare --template=ruby hello_world'\n\nThank you for using Noe, enjoy!\n"
168
+
169
+ ################################################################### SECURITY
170
+
171
+ # The key used to sign this gem. See Gem::Security for details.
172
+ #
173
+ # s.signing_key = nil
174
+
175
+ # The certificate chain used to sign this gem. See Gem::Security for
176
+ # details.
177
+ #
178
+ # s.cert_chain = []
179
+
180
+ ################################################################### RDOC
181
+
182
+ # An ARGV style array of options to RDoc
183
+ #
184
+ # See 'rdoc --help' about this
185
+ #
186
+ s.rdoc_options = []
187
+
188
+ # Extra files to add to RDoc such as README
189
+ #
190
+ s.extra_rdoc_files = Dir["README.md"] + Dir["CHANGELOG.md"] + Dir["LICENCE.md"]
191
+
192
+ end