gemma 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,47 +2,78 @@
2
2
 
3
3
  http://github.com/jdleesmiller/gemma
4
4
 
5
- == DESCRIPTION
6
-
7
- If you are using .gemspecs as intended
8
- (http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/), gemma generates
9
- common rake tasks with default settings extracted from your .gemspec file.
10
-
11
5
  == SYNOPSIS
12
6
 
13
- To create a gem called +mygem+, first create <tt>mygem.gemspec</tt> as
14
- described in http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/.
15
-
16
- For more information about the gemspec, see the Gem::Specification Reference
17
- (http://docs.rubygems.org/read/chapter/20).
18
-
19
- Then put the following at the top of your <tt>Rakefile.rb</tt>:
7
+ Gemma is a gem development helper like hoe and jeweler, but it keeps the
8
+ gemspec in a gemspec file, where it belongs, instead of in your Rakefile.
9
+ This helps your gem to play nicely with commands like gem and bundle, and it
10
+ allows gemma to provide rake tasks with sensible defaults for many common gem
11
+ development tasks.
12
+
13
+ See also http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended for more
14
+ background.
15
+
16
+ === To Create a New Gem
17
+
18
+ Gemma has a simple interactive gem scaffolding tool; run
19
+ gemma new
20
+ and follow the prompts. (Run with -h for help.)
21
+
22
+ This gives you a simple gem template like the following:
23
+
24
+ my_new_gem
25
+ |-- bin
26
+ | +-- my_new_gem # executable (optional)
27
+ |-- .gitignore # helpful if you're using git
28
+ |-- lib
29
+ | |-- my_new_gem
30
+ | | +-- version.rb # the one and only version constant
31
+ | +-- my_new_gem.rb # the main code file
32
+ |-- my_new_gem.gemspec # gem metadata
33
+ |-- Rakefile.rb # development tasks
34
+ |-- README.rdoc # documentation
35
+ +-- test
36
+ +-- test_my_new_gem.rb # unit tests with test/unit (optional)
37
+
38
+ Things you need to change in the template are marked with a +TODO+ tag.
39
+
40
+ === To Modify an Existing Gem to use Gemma
41
+
42
+ First you have to have a <tt>.gemspec</tt> file. See
43
+ http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended
44
+ for a template and advice, and see
45
+ http://docs.rubygems.org/read/chapter/20
46
+ for reference material. If you have been using hoe or jeweler, you already
47
+ have all of the information in your Rakefile, so you just have to move it into
48
+ a <tt>.gemspec</tt> file.
49
+
50
+ Gemma provides rake tasks with sensible defaults based on the contents of
51
+ your gemspec; to enable them, add the following to the top of your +Rakefile+:
20
52
  begin
21
53
  require 'rubygems'
22
54
  require 'gemma'
23
55
 
24
- Gemma::RakeTasks.with_gemspec_file 'mygem.gemspec'
56
+ Gemma::RakeTasks.with_gemspec_file 'my_gem.gemspec'
25
57
  rescue LoadError
26
58
  # Gemma is not installed; print an (optional) message:
27
59
  puts "Install gemma (sudo gem install gemma) for standard rake tasks."
28
60
  end
29
61
 
30
- This gives you a list of standard rake tasks with settings based on your
31
- gemspec, in addition to any that you define yourself. (Note that the yard task
32
- only appears if you have the yard gem installed.)
62
+ This gives you some standard rake tasks, in addition to any that you define
63
+ yourself. (Note that the yard task only appears if you have the yard (yardoc)
64
+ gem installed.)
33
65
 
34
66
  rake clean # Remove any temporary products.
35
67
  rake clobber # Remove any generated file.
36
- rake clobber_rcov # Remove rcov products for rcov
37
- rake clobber_rdoc # Remove rdoc products
38
68
  rake rcov # Analyze code coverage with tests
39
69
  rake rdoc # Build the rdoc HTML Files
40
- rake rerdoc # Force a rebuild of the RDOC files
41
70
  rake test # Run tests
42
71
  rake yard # Generate YARD Documentation
72
+ ... and more!
73
+
74
+ Run <tt>rake -T</tt> for a full list of tasks.
43
75
 
44
- You can customize the generated tasks by passing a block to
45
- +with_gemspec_file+:
76
+ You can customize the standard tasks by passing a block to +with_gemspec_file+:
46
77
 
47
78
  Gemma::RakeTasks.with_gemspec_file 'mygem.gemspec' do |g|
48
79
  g.rdoc.title = 'My Gem Is Great'
@@ -53,34 +84,35 @@ See the gemma API docs for more information.
53
84
  == REQUIREMENTS
54
85
 
55
86
  You must have ruby, rubygems and rake installed. Tasks are also included for
56
- rcov (http://eigenclass.org/hiki.rb?rcov) and yard (http://yardoc.org/).
87
+ rcov (http://eigenclass.org/hiki.rb?rcov) and yard (http://yardoc.org/), if you
88
+ have them installed. Other dependencies are handled automatically.
57
89
 
58
90
  Gemma has been tested on:
59
- * x86-linux (Ubuntu 10.04) with Ruby 1.8.7p249
60
- * x86-linux (rvm) with Ruby 1.9.2p0 (2010-08-18 revision 29034)
91
+ * x86-linux (Ubuntu 10.10) with Ruby 1.8.7p299
92
+ * x86-linux (rvm) with Ruby 1.9.2p0 (2010-08-18 revision 29036)
61
93
  * x86_64-linux (CentOS) with Ruby 1.8.7p72
62
- * with yard 0.5.8 and 0.6.1
63
- * with rcov 0.9.6 and 0.9.9
94
+ * with yard 0.5.8 and 0.6.3
95
+ * with rcov 0.9.7.1 and 0.9.9
64
96
  Gemma has not yet been tested on Windows.
65
97
 
66
- == INSTALL
98
+ == INSTALLATION
67
99
 
68
- You should be able to install with
100
+ On *nix:
69
101
 
70
- gem install gemma
102
+ sudo gem install gemma
71
103
 
72
- Alternatively, you can get the source with
104
+ == DEVELOPMENT
73
105
 
106
+ To get the source and development depencies:
74
107
  git clone git://github.com/jdleesmiller/gemma.git
75
-
76
- and run
77
-
108
+ cd gemma
78
109
  gem build gemma.gemspec
79
- gem install gemma-<version>.gem
110
+ sudo gem install gemma-X.X.X.gem --development
111
+ where X.X.X is the current gemma version.
80
112
 
81
113
  == RELATED PROJECTS
82
114
 
83
- There are many projects that make gem development easier.
115
+ There are many projects that help to automate common gem development tasks.
84
116
  Hoe and Jeweler are the most popular; they generate the gemspec and tasks using
85
117
  a configuration specified in the Rakefile, which is the opposite to what gemma
86
118
  does. Mr Bones is a gem template generator that appears to be similar to Hoe
@@ -90,6 +122,13 @@ and Jeweler, but I haven't used it myself.
90
122
  * http://github.com/technicalpickles/jeweler
91
123
  * http://codeforpeople.rubyforge.org/bones/
92
124
 
125
+ Bundler also provides some support for gem development. It includes a simple gem
126
+ scaffolding generator, and the results are compatible with gemma. It also
127
+ includes some rake extensions for building, installing and releasing gems
128
+ (Bundler::GemHelper). (As of version 1.0.7.)
129
+ * http://gembundler.com
130
+ * http://gembundler.com/rubygems.html
131
+
93
132
  gemesis provides tasks for building, releasing and installing gems based on the
94
133
  gemspec, but (at the time of writing) it doesn't provide tasks for tests and
95
134
  documentation, etc.
@@ -101,6 +140,7 @@ gemspec, and it looks like it should work fairly well with gemma.
101
140
 
102
141
  * http://github.com/luislavena/rake-compiler
103
142
 
143
+
104
144
  Other helpful articles:
105
145
 
106
146
  * http://rubygems.rubyforge.org/rubygems-update/Gem/Version.html
@@ -116,12 +156,20 @@ Other helpful articles:
116
156
  g.yard_dev.options.push('--protected', '--private')
117
157
  end
118
158
  * more tasks (e.g. to publish docs)
119
- * generate gem skeletons
120
159
  * tasks for version control? (see http://github.com/nvie/gitflow)
121
160
  * contributions welcome!
122
161
 
123
162
  == HISTORY
124
163
 
164
+ <em>1.0.0</em>
165
+ * added templates
166
+ * changed rdoc and yard tasks to use +require_paths+ instead of +files+
167
+ * more tests
168
+ * more documentation
169
+
170
+ <em>0.0.2</em>
171
+ * minor fixes
172
+
125
173
  <em>0.0.1</em>
126
174
  * first release
127
175
 
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # = gemma
4
+ #
5
+ # Create new gem from template(s).
6
+ # Options can be specified on the command line or interactively.
7
+ #
8
+ # == Usage
9
+ #
10
+ # gemma new [options]
11
+ #
12
+ # -h, --help::
13
+ # show this message
14
+ #
15
+ # -v, --version::
16
+ # print gemma version and exit
17
+ #
18
+ # --name=name::
19
+ # name of the gem to create; if unspecified, gemma assumes that you want to
20
+ # create the gem interactively, and prompts will be given for any other
21
+ # arguments not specified on the command line.
22
+ #
23
+ # --dir=name::
24
+ # directory to create the new gem in; default is the name of the gem; specify
25
+ # --dir=. to make the current directory the root of the gem.
26
+ #
27
+ # --module=name::
28
+ # name of module that contains the gem contents (e.g. MyNewGem for a gem
29
+ # called my_new_gem); by default, this is computed from the gem name.
30
+ #
31
+ # --template=path::
32
+ # path to template to use instead of default template(s); specify this
33
+ # argument multiple times to copy from several templates (in order).
34
+ #
35
+
36
+ require 'getoptlong'
37
+ require 'gemma'
38
+ require 'highline'
39
+
40
+ #
41
+ # Process options.
42
+ #
43
+
44
+ getopt = GetoptLong.new(
45
+ ['--help', '-h', GetoptLong::NO_ARGUMENT ],
46
+ ['--version', '-v', GetoptLong::NO_ARGUMENT ],
47
+ ['--name', GetoptLong::REQUIRED_ARGUMENT],
48
+ ['--dir', GetoptLong::REQUIRED_ARGUMENT],
49
+ ['--module', GetoptLong::REQUIRED_ARGUMENT],
50
+ ['--template', GetoptLong::REQUIRED_ARGUMENT]
51
+ )
52
+
53
+ opts = {}
54
+ opts['--template'] = []
55
+ getopt.each do |opt, arg|
56
+ case opt
57
+ when '--help' then
58
+ Gemma::Utility.print_usage_from_file_comment __FILE__
59
+ exit
60
+ when '--version' then
61
+ puts "gemma-#{Gemma::VERSION}"
62
+ exit
63
+ when '--template' then
64
+ opts['--template'] << arg
65
+ else
66
+ opts[opt] = arg
67
+ end
68
+ end
69
+
70
+ # Remaining argument should be the command.
71
+ command = ARGV.shift
72
+ unless command && command == "new"
73
+ Gemma::Utility.print_usage_from_file_comment __FILE__
74
+ exit
75
+ end
76
+
77
+ #
78
+ # Create from template; prompt for any missing info.
79
+ #
80
+ hl = HighLine.new
81
+ gt = Gemma::GemFromTemplate.new
82
+
83
+ interactive = !opts['--name'] || opts['--name'].empty?
84
+
85
+ if interactive
86
+ puts
87
+ puts "creating a new gem with gemma..."
88
+ puts
89
+ end
90
+
91
+ unless interactive
92
+ gt.gem_name = opts['--name']
93
+ else
94
+ gt.gem_name = hl.ask('gem name: ') {|q| q.validate = /^.+$/} # weak
95
+ end
96
+
97
+ if opts['--dir']
98
+ gt.dir_name = opts['--dir']
99
+ elsif interactive
100
+ gt.dir_name =
101
+ hl.ask('create gem in directory: ') {|q| q.default = gt.dir_name}
102
+ end
103
+
104
+ if opts['--module']
105
+ gt.module_name = opts['--module']
106
+ elsif interactive
107
+ gt.module_name =
108
+ hl.ask('wrap contents in module called: ') {|q| q.default = gt.module_name}
109
+ end
110
+
111
+ templates = opts['--template']
112
+ if templates.empty?
113
+ if interactive
114
+ templates << 'base' << 'test_unit'
115
+ hl.choose do |menu|
116
+ menu.prompt = "type of gem: "
117
+ menu.shell = true
118
+ menu.choice('library (library scripts only)') do
119
+ # no extras
120
+ end
121
+ menu.choice('program (both library scripts and executable scripts)') do
122
+ templates << 'executable'
123
+ end
124
+ end
125
+ else
126
+ templates.push(*Gemma::GemFromTemplate::BUILTIN_TEMPLATES)
127
+ end
128
+ templates.map!{|name| File.join(Gemma::GemFromTemplate::TEMPLATE_ROOT,name)}
129
+ end
130
+
131
+ puts "creating gem in #{gt.destination_path}"
132
+ gt.create_gem(templates)
133
+ puts "gem created; to get started, see the following TODO tags:"
134
+ Gemma::Utility.rgrep(/TODO/, gt.destination_path)
@@ -1,21 +1,16 @@
1
1
  require 'gemma/version'
2
+ require 'gemma/utility'
2
3
  require 'gemma/options'
3
4
  require 'gemma/rake_tasks'
4
5
 
5
6
  # Load default Rakefile plugins:
6
7
  require 'gemma/rake_tasks/plugin'
7
- require 'gemma/rake_tasks/gem_tasks'
8
8
  require 'gemma/rake_tasks/rcov_tasks'
9
9
  require 'gemma/rake_tasks/rdoc_tasks'
10
+ require 'gemma/rake_tasks/run_tasks'
10
11
  require 'gemma/rake_tasks/yard_tasks'
11
12
  require 'gemma/rake_tasks/test_unit_tasks'
12
13
 
13
- #require 'gemma/utility'
14
-
15
- # Load default Rakefile plugins:
16
- #require 'gemma/rakefile/rdoc'
17
- #require 'gemma/rakefile/yard'
18
- #require 'gemma/rakefile/test_unit'
19
- #require 'gemma/rakefile/rcov'
20
- #require 'gemma/rakefile/gem'
14
+ require 'gemma/conventions'
15
+ require 'gemma/gem_from_template'
21
16
 
@@ -0,0 +1,52 @@
1
+ module Gemma
2
+ #
3
+ # A home for conventions, advice and best practices.
4
+ #
5
+ module Conventions
6
+ #
7
+ # References:
8
+ # http://stackoverflow.com/questions/221320/standard-file-naming-conventions-in-ruby
9
+ # http://containerdiv.wordpress.com/2009/05/24/ruby-gems-vs-rails-plugins/
10
+ #
11
+ # There is probably a required format (regex) for names, but I've never
12
+ # figured out what it is. It should be a valid file/directory name on
13
+ # Windows.
14
+ #
15
+ GOOD_GEM_NAME_TIPS = <<-STRING
16
+ Some tips for good gem names:
17
+ * use lower case
18
+ * separate words with _ (underscore)
19
+ * don't put 'ruby' in the name; this is implied, because it's a rubygem
20
+
21
+ It is also common to use a hyphen (-) to separate words.
22
+
23
+ If your gem name is my_new_gem, it can be installed with
24
+ sudo gem install my_new_gem
25
+ used in scripts with
26
+ require 'rubygems'
27
+ require 'my_new_gem'
28
+ and its contents should be contained in a module or class called MyNewGem.
29
+ STRING
30
+
31
+ #
32
+ # Check gem naming conventions; this is sufficient but not necessary for
33
+ # the gem name to be valid.
34
+ #
35
+ # @return [Boolean]
36
+ #
37
+ def self.good_gem_name? gem_name
38
+ gem_name =~ /^[a-z0-9]+[a-z0-9_\-]*$/
39
+ end
40
+
41
+ #
42
+ # Convert from underscore-lower-case form to camel-case form; hyphens are
43
+ # also converted to camel case.
44
+ #
45
+ # @return [String]
46
+ #
47
+ def self.gem_name_to_module_name gem_name
48
+ gem_name.gsub(/(?:^|_|-)([a-z0-9])/) { $1.upcase }
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,134 @@
1
+ require 'erb'
2
+ require 'fileutils'
3
+ require 'yaml'
4
+
5
+ module Gemma
6
+ #
7
+ # Configurable gem skeleton from a template.
8
+ #
9
+ class GemFromTemplate
10
+ #
11
+ # Location of built-in templates.
12
+ #
13
+ TEMPLATE_ROOT = File.join(File.dirname(__FILE__), '..', '..', 'template')
14
+
15
+ #
16
+ # Built-in template names (not full paths). Order may be significant if
17
+ # there are files that occur in multiple templates (files in earlier
18
+ # templates are overwritten by those in later templates, at present).
19
+ #
20
+ BUILTIN_TEMPLATES = %w(base executable test_unit)
21
+
22
+ def initialize
23
+ @gem_name = nil
24
+ @module_name = nil
25
+ @dir_name = nil
26
+ end
27
+
28
+ #
29
+ # The gem name to be used in the gemspec; it also gives defaults for the
30
+ # directory name and the gem's module name.
31
+ #
32
+ # @return [String]
33
+ #
34
+ attr_accessor :gem_name
35
+
36
+ #
37
+ # Gem name is consistent with naming conventions.
38
+ #
39
+ # @return [Boolean]
40
+ #
41
+ def good_gem_name?
42
+ Conventions.good_gem_name? self.gem_name
43
+ end
44
+
45
+ #
46
+ # Guess main module (or class) name from gem name (e.g. MyNewGem from
47
+ # my_new_gem). The gem contents should be contained in this module / class.
48
+ #
49
+ # @return [String]
50
+ #
51
+ def module_name
52
+ @module_name || Conventions.gem_name_to_module_name(self.gem_name)
53
+ end
54
+
55
+ #
56
+ # Override default module name; set to nil to get back to default.
57
+ #
58
+ attr_writer :module_name
59
+
60
+ #
61
+ # Name of the root directory of the gem to be created.
62
+ #
63
+ # @return [String]
64
+ #
65
+ def dir_name
66
+ @dir_name || @gem_name
67
+ end
68
+
69
+ #
70
+ # Override default directory name; set to nil to get back to default.
71
+ #
72
+ attr_writer :dir_name
73
+
74
+ #
75
+ # Full path of root of the gem to be created.
76
+ #
77
+ def destination_path
78
+ File.expand_path(File.join('.',self.dir_name))
79
+ end
80
+
81
+ #
82
+ # Gem version requirement for the development dependency on gemma.
83
+ #
84
+ # @return [String]
85
+ #
86
+ def gemma_version_requirement
87
+ "~> #{Gemma::VERSION.split(/\./).take(2).join('.')}"
88
+ end
89
+
90
+ #
91
+ # Copy given templates to +destination_path+ and run erb where needed.
92
+ #
93
+ def create_gem template_paths, destination_path=self.destination_path
94
+ raise "destination #{destination_path} exists" if File.exists?(
95
+ destination_path)
96
+
97
+ # Copy templates in.
98
+ FileUtils.mkdir_p destination_path
99
+ for path in template_paths
100
+ FileUtils.cp_r File.join(path,'.'), destination_path
101
+ end
102
+
103
+ Dir.chdir destination_path do
104
+ dirs = Dir["**/*"].select { |f| File.directory? f }.sort
105
+ dirs.grep(/gem_name/).each do |file|
106
+ FileUtils.mv file, file.gsub(/gem_name/, gem_name)
107
+ end
108
+
109
+ files = (Dir["**/*"] + Dir["**/.*"]).select { |f| File.file? f }.sort
110
+ FileUtils.chmod 0644, files
111
+ FileUtils.chmod 0755, files.select{|f| File.dirname(f) == 'bin'}
112
+ files.each do |file|
113
+ # Rename files with names that depend on the gem name.
114
+ if file =~ /gem_name/
115
+ new_file = file.sub(/gem_name/, gem_name)
116
+ FileUtils.mv file, new_file
117
+ file = new_file
118
+ end
119
+
120
+ # Run erb to customize each file.
121
+ if File.extname(file) == ".erb"
122
+ erb_file = File.read file
123
+ File.open file, "w" do |f|
124
+ erb = ERB.new(erb_file)
125
+ erb.filename = file
126
+ f.puts erb.result(binding)
127
+ end
128
+ FileUtils.mv file, file.sub(/\.erb$/, '')
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end