noe 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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