├── Readme.md ├── bin └── gemerate └── gemerator.gemspec /Readme.md: -------------------------------------------------------------------------------- 1 | Like NewGem, but way simpler, leaves no traces, extremely minimal boiler plate. 2 | Automatically handles extensions for Rack, Yard, Sinatra, etc correctly. 3 | 4 | Inspired by [Steve Klabnik](http://steveklabnik.com/)'s 5 | [Making Ruby Gems](http://timelessrepo.com/making-ruby-gems). 6 | 7 | Only point where it is opinionated: It sets up RSpec. But all you have to do if 8 | you don't like that, is changing a single line in the generated Rakefile. 9 | 10 | Feel free to [contribute](http://github.com/rkh/gemerator). 11 | 12 | # Usage 13 | 14 | gemerate GEM_NAME 15 | 16 | Simpe example: 17 | 18 | $ gemerate foo 19 | ... 20 | $ tree foo 21 | foo 22 | ├── Gemfile 23 | ├── License 24 | ├── README.md 25 | ├── Rakefile 26 | ├── foo.gemspec 27 | ├── lib 28 | │ ├── foo 29 | │ │ └── version.rb 30 | │ └── foo.rb 31 | └── spec 32 | └── foo_spec.rb 33 | 34 | 3 directories, 8 files 35 | 36 | For an extension: 37 | 38 | $ gemerate rack-foo 39 | ... 40 | $ tree rack-foo/ 41 | rack-foo/ 42 | ├── Gemfile 43 | ├── License 44 | ├── README.md 45 | ├── Rakefile 46 | ├── lib 47 | │ ├── rack 48 | │ │ ├── foo 49 | │ │ │ └── version.rb 50 | │ │ └── foo.rb 51 | │ └── rack-foo.rb 52 | ├── rack-foo.gemspec 53 | └── spec 54 | └── rack_foo_spec.rb 55 | 56 | 4 directories, 9 files 57 | 58 | Per default, `rack-foo.gemspec` will automatically depend on `rack` and 59 | `lib/rack/foo.rb` will require `rack`. 60 | 61 | # Why not `bundle gem`? 62 | 63 | `bundle gem` is fine, but just too little boilerplate, here is what is 64 | different: 65 | 66 | * A `rake test` is ready to go. 67 | * The gemspec does not include shell-outs, file list is not generic (this is a 68 | feature!) 69 | * Less TODOs all over the place you have to remove 70 | * No rubbish in the `.gitignore` 71 | * Includes rake task to update files/authors/e-mails from git and version from 72 | lib (similar to what Sinatra/Tilt/sinatra-contrib and so on do) 73 | * Internally, the version is not a string to avoid alphanumerical comparison 74 | (similar to what Rails does) 75 | * You can do `QUICK=1 bundle install` to avoid fetching from RubyForge 76 | * Some logic to generate better boilerplate for extensions (Rack middleware, 77 | Sinatra extensions, ...) has been added. 78 | 79 | Apart from that, it's pretty much the same. Oh, wait, and it works without 80 | bundler. 81 | 82 | # Installation 83 | 84 | gem install gemerator 85 | -------------------------------------------------------------------------------- /bin/gemerate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'backports' 3 | require 'fileutils' 4 | 5 | if ARGV.count != 1 6 | $stderr.puts "Usage: #{$0} GEM_NAME" 7 | exit 1 8 | end 9 | 10 | name = ARGV.first.gsub('-', '/').underscore 11 | folder = name.gsub '/', '-' 12 | files = File.read(__FILE__).scan /\n\[(\S+)\]\n((?:(?!\n\[\S+\]).)*)/m 13 | user = `git config github.user || echo GITHUB_USER`.strip 14 | author = `git config user.name || echo Unknown`.strip 15 | ipolate = proc do |str| 16 | str.gsub("%Lib::Name%", name.camelize).gsub("%lib/name%", name). 17 | gsub("%lib_name%", name.gsub('/', '_')).gsub("%GITHUB_USER%", user). 18 | gsub("%lib-name%", folder).gsub("%LIB_NAME%", name.upcase). 19 | gsub(/(.*)%lib%(.*\n)/) { "#$1%s#$2" % name[/^[^\/]+/] if name['/'] }. 20 | gsub("%Author Name%", author).gsub("%year%", Time.now.year.to_s). 21 | gsub(/%mod%\n(.*\n)?%mod%/m) do 22 | lines = $1.to_s.split "\n" 23 | lines.map! { |l| " " * name.count('/') + l } 24 | pre, post = "", "" 25 | name.camelize.split('::').each_with_index do |nested, index| 26 | indent = " " * index 27 | post, pre = "\n#{post}", "#{pre}\n" unless pre.empty? 28 | post, pre = "#{indent}end#{post}", "#{pre}#{indent}module #{nested}" 29 | end 30 | lines.unshift(pre).push(post).join("\n") 31 | end 32 | end 33 | 34 | files.each do |file, content| 35 | file = File.join(folder, ipolate[file]) 36 | next if File.exist? file 37 | FileUtils::Verbose.mkdir_p File.dirname(file) 38 | $stderr.puts "touch #{file}" 39 | File.open(file, 'w') { |f| f << ipolate[content] } 40 | end 41 | 42 | Dir.chdir(folder) do 43 | system "git init && git add . && git commit -m 'initial commit'" 44 | system "ruby -S rake gemspec" 45 | system "git commit -a -m 'initial commit' --amend" 46 | end 47 | 48 | __END__ 49 | 50 | [%lib-name%.gemspec] 51 | # Run `rake %lib-name%.gemspec` to update the gemspec. 52 | Gem::Specification.new do |s| 53 | # general infos 54 | s.name = "%lib-name%" 55 | s.version = "0.0.1" 56 | s.description = "No Description Yet" 57 | s.homepage = "http://github.com/%GITHUB_USER%/%lib-name%" 58 | s.summary = s.description 59 | 60 | # generated from git shortlog -sn 61 | s.authors = [ 62 | ] 63 | 64 | # generated from git shortlog -sne 65 | s.email = [ 66 | ] 67 | 68 | # generated from git ls-files 69 | s.files = [ 70 | ] 71 | 72 | # dependencies 73 | s.add_dependency "%lib%" 74 | s.add_development_dependency "rspec", "~> 2.0" 75 | end 76 | 77 | [Rakefile] 78 | $LOAD_PATH.unshift File.expand_path('../lib', __FILE__) 79 | 80 | begin 81 | require 'bundler' 82 | Bundler::GemHelper.install_tasks 83 | rescue LoadError => e 84 | $stderr.puts e 85 | end 86 | 87 | desc "run specs" 88 | task(:spec) { ruby '-S rspec spec' } 89 | 90 | desc "generate gemspec" 91 | task '%lib-name%.gemspec' do 92 | require '%lib/name%/version' 93 | content = File.read '%lib-name%.gemspec' 94 | 95 | fields = { 96 | :authors => `git shortlog -sn`.scan(/[^\d\s].*/), 97 | :email => `git shortlog -sne`.scan(/[^<]+@[^>]+/), 98 | :files => `git ls-files`.split("\n").reject { |f| f =~ /^(\.|Gemfile)/ } 99 | } 100 | 101 | fields.each do |field, values| 102 | updated = " s.#{field} = [" 103 | updated << values.map { |v| "\n %p" % v }.join(',') 104 | updated << "\n ]" 105 | content.sub!(/ s\.#{field} = \[\n( .*\n)* \]/, updated) 106 | end 107 | 108 | content.sub! /(s\.version.*=\s+).*/, "\\1\"#{%Lib::Name%::VERSION}\"" 109 | File.open('%lib-name%.gemspec', 'w') { |f| f << content } 110 | end 111 | 112 | task :gemspec => '%lib-name%.gemspec' 113 | task :default => :spec 114 | task :test => :spec 115 | 116 | [Gemfile] 117 | source "http://rubygems.org" unless ENV['QUICK'] 118 | gemspec 119 | 120 | [License] 121 | Copyright (c) %year% %Author Name% 122 | 123 | Permission is hereby granted, free of charge, to any person obtaining 124 | a copy of this software and associated documentation files (the 125 | 'Software'), to deal in the Software without restriction, including 126 | without limitation the rights to use, copy, modify, merge, publish, 127 | distribute, sublicense, and/or sell copies of the Software, and to 128 | permit persons to whom the Software is furnished to do so, subject to 129 | the following conditions: 130 | 131 | The above copyright notice and this permission notice shall be 132 | included in all copies or substantial portions of the Software. 133 | 134 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 135 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 136 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 137 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 138 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 139 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 140 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 141 | 142 | [README.md] 143 | I have no clue what this library does. 144 | 145 | # Pro Tip 146 | 147 | To not have too many boilerplate commits (so you avoid ppl from thinking "WTF? 148 | Why did he/she start with rspec but instantly switched to minitest, and what's 149 | all that nonsense in the readme?") use `git commit --amend` to make changes in 150 | the initial commit rather than creating new commits. Just saying. 151 | 152 | # Usage 153 | 154 | ``` ruby 155 | require '%lib/name%' 156 | fail("Dunno how to use %p" % %Lib::Name%) 157 | ``` 158 | 159 | # Installation 160 | 161 | gem install %lib-name% 162 | 163 | # TODO 164 | 165 | * Write code and documentation 166 | * Fix project description in gemspec 167 | * Change testing framework if necessary (see Rakefile, currently RSpec) 168 | * Adjust %lib-name%.gemspec if your github name is not %GITHUB_USER% 169 | * Adjust License if your real name is not %Author Name% 170 | 171 | [.gitignore] 172 | # please add general patterns to your global ignore list 173 | # see https://github.com/github/gitignore#readme 174 | 175 | [lib/%lib/name%.rb] 176 | require '%lib/name%/version' 177 | require '%lib%' 178 | 179 | %mod% 180 | %mod% 181 | 182 | [lib/%lib-name%.rb] 183 | require "%lib/name%" 184 | 185 | [spec/%lib_name%_spec.rb] 186 | require '%lib/name%' 187 | 188 | describe %Lib::Name% do 189 | it "should behave" 190 | end 191 | 192 | [lib/%lib/name%/version.rb] 193 | %mod% 194 | def self.version 195 | VERSION 196 | end 197 | 198 | module VERSION 199 | extend Comparable 200 | 201 | MAJOR = 0 202 | MINOR = 0 203 | TINY = 1 204 | SIGNATURE = [MAJOR, MINOR, TINY] 205 | STRING = SIGNATURE.join '.' 206 | 207 | def self.major; MAJOR end 208 | def self.minor; MINOR end 209 | def self.tiny; TINY end 210 | def self.to_s; STRING end 211 | 212 | def self.hash 213 | STRING.hash 214 | end 215 | 216 | def self.<=>(other) 217 | other = other.split('.').map { |i| i.to_i } if other.respond_to? :split 218 | SIGNATURE <=> Array(other) 219 | end 220 | 221 | def self.inspect 222 | STRING.inspect 223 | end 224 | 225 | def self.respond_to?(meth, *) 226 | meth.to_s !~ /^__|^to_str$/ and STRING.respond_to? meth unless super 227 | end 228 | 229 | def self.method_missing(meth, *args, &block) 230 | return super unless STRING.respond_to?(meth) 231 | STRING.send(meth, *args, &block) 232 | end 233 | end 234 | %mod% 235 | -------------------------------------------------------------------------------- /gemerator.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = "gemerator" 3 | s.version = "0.2.0" 4 | s.description = "Like NewGem, but way simpler." 5 | s.homepage = "http://github.com/rkh/gemerator" 6 | s.authors = %w[Konstantin Haase] 7 | s.email = %w[konstantin.mailinglists@googlemail.com] 8 | s.files = %w[Readme.md gemerator.gemspec bin/gemerate] 9 | s.executables = %w[gemerate] 10 | s.summary = <<-SUMMARY 11 | Like NewGem, but way simpler, leaves no traces, extremely minimal boiler 12 | plate. Automatically handles extensions for Rack, Yard, Sinatra, etc 13 | correctly. 14 | SUMMARY 15 | 16 | s.add_dependency('backports', '>= 2.2.1') 17 | end --------------------------------------------------------------------------------