noe 1.0.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 ADDED
@@ -0,0 +1,5 @@
1
+ # 1.0.0 / 2011-01-10
2
+
3
+ * Enhancements
4
+
5
+ * Birthday!
data/LICENCE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 - Bernard Lambeau
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # Noe - A simple and extensible project generator
2
+
3
+ Noe helps development by providing support for project templates and instantiation.
4
+
5
+ ## Why?
6
+
7
+ I'm pretty sure I have seen announcements for similar projects on ruby-lang in the past,
8
+ but I could not find them anymore... so I started mine. Please let me know alternatives
9
+ and I'll add them below:
10
+
11
+ See also:
12
+
13
+ * ...
14
+
15
+ Other reasons:
16
+
17
+ * Noe is agnostic: it does not make any assumption about project semantics
18
+ * Noe does not restrict itself to generation of ruby projects
19
+ * Noe is not required at runtime: once your project is generated you're done
20
+ * I don't like magic
21
+
22
+ ## Getting started
23
+
24
+ [sudo] gem install noe
25
+ [noe --help]
26
+ [noe help install]
27
+ noe install
28
+
29
+ Have a loot at ~/.noerc and ~/.noe for configuration and a default ruby
30
+ template.
31
+
32
+ ## Usage summary
33
+
34
+ Maintain your templates under ~/.noe (or what you provided at installation time). Have a
35
+ look at github to find xxx.noe projects to find well-designed/documented templates for
36
+ specific needs.
37
+
38
+ To create a fresh new project:
39
+
40
+ # Given a template xxx under ~/.noe/xxx
41
+ noe create --template=xxx foo
42
+ cd foo
43
+
44
+ # Edit the template configuration foo/foo.noespec
45
+ edit foo/foo.noespec
46
+
47
+ # Launch template instantiation
48
+ noe go
49
+
50
+ That's it! But also have a look at 'noe help create' and 'not help go' for additional
51
+ options.
52
+
53
+ ## About templates
54
+
55
+ Under ~/.noe, a valid template folder (say xxx) has the following structure
56
+
57
+ xxx # Template name
58
+ README(.md|.txt|...) # Information about the template and it's usage
59
+ CHANGELOG(.md|.txt|...) # Change information
60
+ noespec.yaml # Template specification
61
+ src # Source folder, contains files to be instantiated
62
+ ... # [everything that will be instantiated]
63
+
64
+ ### noespec.yaml
65
+
66
+ The noespec.yaml file of a template is used to formally describe the template. When a project
67
+ (say foo) is created (see 'noe create') using a template (say ruby) the file
68
+ ~/.noe/ruby/noespec.yaml is used to generate foo/foo.noespec. The later is then used by
69
+ 'noe go' to instantiate the project.
70
+
71
+ The noespec.yaml file should ressemble something like this:
72
+
73
+ # DO NOT TOUCH 'name' entry and specify the other
74
+ template-info:
75
+ name: !{template_name}
76
+ description: ...
77
+ version: ...
78
+ author: ...
79
+
80
+ #
81
+ # The following is a hash of template-related variables. They are
82
+ # used to provide dynamic file names and instantiate file contents.
83
+ #
84
+ # Current version of Noe only supports variable names matching /[a-z]+/
85
+ #
86
+ variables:
87
+ ...
88
+
89
+ Have a look at ~/.noe/ruby/noespec.yaml and ~/.noe/ruby/src for an example.
90
+
91
+ ### Instantiation process
92
+
93
+ The instantiation process is really simple. Given the variables described in the noespec.yaml
94
+ file (for which values are specified in your .noespec file) templates can use the following
95
+ meta-constructions:
96
+
97
+ * Template files and directories containing `__variable__` in their name are automatically
98
+ renamed (`__variable__` is replaced by the corresponding value).
99
+ * All template files are instantiated by [wlang](https://github.com/blambeau/wlang). You don't
100
+ have to know wlang in depth. You simply have to know that `!{ruby_expression}` in a file is
101
+ replaced by the expression evaluation. Variables are automatically in scope of such expressions,
102
+ so that `!{variable}` is replaced by its value.
103
+
104
+ ## Contributing
105
+
106
+ Fork Noe on github! I'm particularly interested in the following enhancements:
107
+
108
+ * Extend test coverage, which is ugly so far.
109
+ * Enhance the default ruby template, but remember "documentation matters, not magic!"
110
+ * Add support for other generators than _wlang_
111
+ * Add support for multi-generated files from arrays in .noespec files
112
+ * ...
113
+
114
+ If you think that your template is worth considering for (ruby, rails, js, latex, or anything
115
+ else) please let me known and I'll add it to the list below.
116
+
117
+ * ...
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require "rake/gempackagetask"
2
+ require "rspec/core/rake_task"
3
+ require "yard"
4
+
5
+ # We run tests by default
6
+ task :default => :spec
7
+
8
+ desc "Run all examples"
9
+ RSpec::Core::RakeTask.new(:spec) do |t|
10
+ t.rspec_opts = %w[--color]
11
+ t.verbose = false
12
+ end
13
+
14
+ # About yard documentation
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.files = ['lib/**/*.rb']
17
+ t.options = ['--output-dir', 'doc/api', '-', "README.md", "CHANGELOG.md"]
18
+ end
19
+
20
+ desc "Create the .gem package"
21
+ $spec = Kernel.eval(File.read(File.expand_path('../noe.gemspec', __FILE__)))
22
+ Rake::GemPackageTask.new($spec) do |pkg|
23
+ pkg.need_tar = true
24
+ end
data/bin/noe ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'noe'
4
+ Noe::Main.run(ARGV)
5
+
data/lib/noe.rb ADDED
@@ -0,0 +1,39 @@
1
+ module Noe
2
+
3
+ # Noe's version
4
+ VERSION = "1.0.0".freeze
5
+
6
+ # Requires some gem
7
+ def self.require(gem_name, version = nil, retried = false)
8
+ begin
9
+ Kernel.require gem_name
10
+ rescue LoadError
11
+ if retried
12
+ raise
13
+ else
14
+ retried = true
15
+ require 'rubygems'
16
+ gem gem_name, version
17
+ retry
18
+ end
19
+ end
20
+ end
21
+
22
+ class Error < StandardError; end
23
+
24
+ end # module Noe
25
+
26
+ Noe.require('quickl', ">= 0.2.0")
27
+ Noe.require('wlang', ">= 0.9")
28
+ require 'yaml'
29
+ require 'fileutils'
30
+ require 'noe/config'
31
+ require 'noe/template'
32
+ require 'noe/main'
33
+ require 'noe/commons'
34
+ require 'noe/install'
35
+ require 'noe/help'
36
+ require 'noe/list'
37
+ require 'noe/create'
38
+ require 'noe/go'
39
+
@@ -0,0 +1,18 @@
1
+ module Noe
2
+ module Commons
3
+
4
+ # Returns configuration to use
5
+ def config
6
+ requester.config
7
+ end
8
+
9
+ def templates_dir
10
+ config.templates_dir
11
+ end
12
+
13
+ def template(name = config.default)
14
+ Template.new(File.join(templates_dir, name))
15
+ end
16
+
17
+ end # module Commons
18
+ end # module Noe
data/lib/noe/config.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Noe
2
+ class Config
3
+
4
+ # Default configuration hash
5
+ DEFAULT_CONFIG = {
6
+ 'version' => Noe::VERSION,
7
+ 'templates-dir' => File.expand_path('../../../templates', __FILE__),
8
+ 'default' => 'ruby'
9
+ }
10
+
11
+ # Path to the configuration file
12
+ attr_reader :file
13
+
14
+ # Loaded configuration hash
15
+ attr_reader :config
16
+
17
+ # Creates a config instance from some .noerc file
18
+ def initialize(file = nil)
19
+ @config = DEFAULT_CONFIG
20
+ @file = file
21
+ __load unless file.nil?
22
+ end
23
+
24
+ # Loads configuration from YAML file
25
+ def __load
26
+ if File.file?(file) and File.readable?(file)
27
+ loaded = YAML::load(File.read(file))
28
+ if loaded.is_a?(Hash)
29
+ @config.merge!(loaded)
30
+ else
31
+ raise Noe::Error, "Corrupted or invalid config file: #{file}"
32
+ end
33
+ else
34
+ raise Noe::Error, "Not a file or not readable: #{file}"
35
+ end
36
+ end
37
+
38
+ # Returns folder where templates are located
39
+ def templates_dir
40
+ dir = config['templates-dir']
41
+ if File.directory?(dir) and File.readable?(dir)
42
+ dir
43
+ else
44
+ raise Noe::Error, "Invalid noe config, not a directory or unreadable: #{dir}"
45
+ end
46
+ end
47
+
48
+ # Returns the name of the default template to use
49
+ def default
50
+ config['default']
51
+ end
52
+
53
+ # Sets the name of the default template to use
54
+ def default=(name)
55
+ config['default'] = name
56
+ end
57
+
58
+ private :__load
59
+ end # class Config
60
+ end # module Noe
@@ -0,0 +1,29 @@
1
+ #
2
+ # .noerc - Configuration of Noe, a simple project generator.
3
+ #
4
+ # This file provides Noe's configuration. More information can be
5
+ # found at https://github.com/blambeau/noe.
6
+ #
7
+ # If you want to regenerate a fresh new file, simply run:
8
+ #
9
+ # noe install
10
+ #
11
+
12
+ # This is Noe's version, change only if you known what you
13
+ # are doing!
14
+ version:
15
+ !{Noe::VERSION}
16
+
17
+ #
18
+ # Absolute path to the folder containing Noe's templates.
19
+ #
20
+ templates-dir:
21
+ !{templates_dir}
22
+
23
+ #
24
+ # Name of the default template that you want to use. Commands
25
+ # that need a template (see 'noe help create', for example) use this
26
+ # by default, and allow overriding this via a --template=NAME option.
27
+ #
28
+ default:
29
+ ruby
data/lib/noe/create.rb ADDED
@@ -0,0 +1,77 @@
1
+ require 'fileutils'
2
+ module Noe
3
+ class Main
4
+ #
5
+ # Create a fresh 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 guides you with the creation of a new project whose
15
+ # name is given as first argument. A new folder is created and a .noespec
16
+ # file is generated in it. The template specified in ~/.noerc under :default
17
+ # is used by default. Use --template to override this.
18
+ #
19
+ # After creation, you'll have to edit the generated .noespec file then run
20
+ # 'noe go' in the new directory.
21
+ #
22
+ # TYPICAL USAGE
23
+ #
24
+ # # start creation of a ruby project
25
+ # noe create --ruby hello_world
26
+ # cd hello_world
27
+ #
28
+ # # edit the configuration
29
+ # edit hello_world.noespec
30
+ #
31
+ # # launch template generation
32
+ # noe go
33
+ #
34
+ class Create < Quickl::Command(__FILE__, __LINE__)
35
+ include Noe::Commons
36
+
37
+ # Install options
38
+ options do |opt|
39
+ opt.on('--template=TPLNAME',
40
+ 'Set the template to use') do |name|
41
+ config.default = name
42
+ end
43
+ end
44
+
45
+ def execute(args)
46
+ raise Quickl::Help unless args.size == 1
47
+
48
+ # get project name and check folder and template
49
+ pname = args.first
50
+ if File.exists?(pname)
51
+ raise Noe::Error, "File #{pname} already exists, remove it first."
52
+ end
53
+ tpl = template
54
+
55
+ # create folder now
56
+ FileUtils.mkdir(pname)
57
+
58
+ # instantiate the configuration
59
+ File.open(File.join(pname, "#{pname}.noespec"), 'w') do |out|
60
+ context = {'template_name' => tpl.name}
61
+ out << WLang::file_instantiate(tpl.spec_file, context, "wlang/active-string")
62
+ end
63
+
64
+ # what's next
65
+ puts "Project successfully started !"
66
+ puts
67
+ puts "What's next?"
68
+ puts " * cd #{pname}"
69
+ puts " * vim #{pname}.noespec"
70
+ puts " * noe go"
71
+ puts
72
+ puts "Thank you for using Noe (v#{Noe::VERSION}), enjoy!"
73
+ end
74
+
75
+ end # class Create
76
+ end # class Main
77
+ end # module Noe
data/lib/noe/go.rb ADDED
@@ -0,0 +1,194 @@
1
+ module Noe
2
+ class Main
3
+ #
4
+ # Instantiate a project template using a .noespec file.
5
+ #
6
+ # SYNOPSIS
7
+ # #{program_name} #{command_name} [options] [SPEC_FILE]
8
+ #
9
+ # OPTIONS
10
+ # #{summarized_options}
11
+ #
12
+ # DESCRIPTION
13
+ # This command instantiate a project template using a .noespec file
14
+ # given as first argument. If no spec file is specified, Noe expects
15
+ # one .noespec file to be present in the current directory and uses it.
16
+ #
17
+ # This command is generally used immediately after invoking 'create',
18
+ # on an almost empty directory. By default it safely fails if any file
19
+ # or directory would be overriden by the instantiation process. This
20
+ # safe behavior can be bypassed through the --force and --add-only
21
+ # options.
22
+ #
23
+ # TYPICAL USAGE
24
+ #
25
+ # When a fresh new project is created, this command is typically used
26
+ # with the following scenario
27
+ #
28
+ # noe create --ruby hello_world
29
+ # cd hello_world
30
+ # edit hello_world.noespec
31
+ # noe go
32
+ #
33
+ # If you modify your .noespec file and want to force overriding of all
34
+ # files:
35
+ #
36
+ # noe go --force
37
+ #
38
+ # If you want to regenerate some files only (README and gemspec, for
39
+ # example):
40
+ #
41
+ # rm README.md hello_world.gemspec
42
+ # noe go --add-only
43
+ #
44
+ class Go < Quickl::Command(__FILE__, __LINE__)
45
+ include Noe::Commons
46
+
47
+ # Dry-run mode ?
48
+ attr_reader :dry_run
49
+
50
+ # Force mode ?
51
+ attr_reader :force
52
+
53
+ # Only make additions ?
54
+ attr_reader :adds_only
55
+
56
+ # Install options
57
+ options do |opt|
58
+ @dry_run = false
59
+ opt.on('--dry-run', '-d',
60
+ "Say what would be done but don't do it"){
61
+ @dry_run = true
62
+ }
63
+ @force = false
64
+ opt.on('--force', '-f',
65
+ "Force overriding on all existing files"){
66
+ @force = true
67
+ }
68
+ @adds_only = false
69
+ opt.on('--add-only', '-a',
70
+ "Only make additions, do not override any existing file"){
71
+ @adds_only = true
72
+ }
73
+ end
74
+
75
+ def build_one(entry, variables)
76
+ relocated = entry.relocate(variables)
77
+ todo = []
78
+
79
+ # The file already exists, we should maybe do something
80
+ if File.exists?(relocated)
81
+ if force
82
+ unless entry.directory? and File.directory?(relocated)
83
+ todo << Rm.new(entry, variables)
84
+ end
85
+ elsif adds_only
86
+ return todo
87
+ else
88
+ raise Noe::Error, "Noe aborted: file #{relocated} already exists.\n"\
89
+ "Use --force to override."
90
+ end
91
+ end
92
+
93
+ # Create directories
94
+ if entry.directory? and not(File.exists?(relocated))
95
+ todo << MkDir.new(entry, variables)
96
+
97
+ # Create files
98
+ elsif entry.file?
99
+ todo << FileInstantiate.new(entry, variables)
100
+
101
+ end
102
+ todo
103
+ end
104
+
105
+ def execute(args)
106
+ raise Quickl::Help if args.size > 1
107
+
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
+ # Load spec now
120
+ spec = YAML::load(File.read(spec_file))
121
+ template = template(spec['template-info']['name'])
122
+ variables = spec['variables']
123
+
124
+ # Build what has to be done
125
+ commands = template.collect{|entry|
126
+ build_one(entry, variables)
127
+ }.flatten
128
+
129
+ # let's go now
130
+ if dry_run
131
+ commands.each{|c| puts c}
132
+ else
133
+ commands.each{|c| c.run}
134
+ end
135
+
136
+ end
137
+
138
+ class DoSomething
139
+
140
+ attr_reader :entry
141
+ attr_reader :variables
142
+
143
+ def initialize(entry, variables)
144
+ @entry, @variables = entry, variables
145
+ end
146
+
147
+ def relocated
148
+ entry.relocate(variables)
149
+ end
150
+
151
+ end
152
+
153
+ class MkDir < DoSomething
154
+
155
+ def run
156
+ FileUtils.mkdir relocated
157
+ end
158
+
159
+ def to_s
160
+ "mkdir #{relocated}"
161
+ end
162
+
163
+ end # class MkDir
164
+
165
+ class Rm < DoSomething
166
+
167
+ def run
168
+ FileUtils.rm_rf relocated
169
+ end
170
+
171
+ def to_s
172
+ "rm -rf #{relocated}"
173
+ end
174
+
175
+ end # class Rm
176
+
177
+ class FileInstantiate < DoSomething
178
+
179
+ def run
180
+ File.open(relocated, 'w') do |out|
181
+ dialect = "wlang/active-string"
182
+ out << WLang::file_instantiate(entry.realpath, variables, dialect)
183
+ end
184
+ end
185
+
186
+ def to_s
187
+ "wlang #{entry.path} > #{relocated}"
188
+ end
189
+
190
+ end
191
+
192
+ end # class Go
193
+ end # class Main
194
+ end # module Noe