Skip to content

Commit

Permalink
Turning Gemify into a gemspec editor
Browse files Browse the repository at this point in the history
  • Loading branch information
judofyr committed Jan 9, 2010
1 parent c8ba4a4 commit fb8f016
Show file tree
Hide file tree
Showing 11 changed files with 1,004 additions and 642 deletions.
13 changes: 0 additions & 13 deletions MANIFEST

This file was deleted.

104 changes: 47 additions & 57 deletions bin/gemify
@@ -1,68 +1,58 @@
#!/usr/bin/env ruby

$:.unshift File.dirname(__FILE__)+"/../lib"

$:.unshift File.dirname(__FILE__) + '/../lib'
require 'gemify'
require 'optparse'
require 'trollop'

options = {}
options[:manifest] = :auto
options[:interactive] = true # Interactive by default
options[:included_files] = false
options[:ui] = "cli"
gemspec = nil
actions = [:new, :update, :manifest, :generate_manifest]

parser = OptionParser.new do |opts|
opts.banner = "Usage: #{opts.program_name} [options]"
opts.separator ""
opts.separator "Options:"
opts.on("-h", "--help", "Shows this message") do |val|
$stdout.puts opts
exit(0)
end
opts.on("-I", "--no-interactive", "Automaticically builds the gem") do |val|
options[:interactive] = false
end
opts.on("-i", "--included-files", "Show included files") do |val|
options[:included_files] = true
end
opts.on("-m", "--manifest TYPE",
"Where to look for the manifest:",
" file = Inside a MANIFEST-file",
" vcs = Search inside different VCS",
" basic = lib/ + bin/",
" auto (default) = Tries all of the above",
" ",
"You can also give it a single VCS:",
"git, darcs, hg, bzr, svn or cvs") do |val|
options[:manifest] = val.to_sym
end
opts.on("-u", "--ui NAME", "Specifies user interface") do |name|
options[:ui] = name
end
opts = Trollop.options do
version "gemify 0.3"
banner <<-EOS
Usage: #{$0} [OPTION]... [FILE]
A simple gemspec editor
EOS

opt :new, "Create a new gemspec"
opt :update, "Update the manifest"
opt :manifest, "Dump the manifest"
opt :generate_manifest, "Generate a manifest", :type => :string
end

begin
parser.parse!(ARGV)
rescue => e
$stderr.puts "Error: #{e.message}"
$stdout.puts parser
exit -1
end
action = opts.detect { |opt, val| actions.include?(opt) && val }
action = action[0] unless action.nil?

begin
ui = Gemify::UI.use(options[:ui]).new(options[:manifest])
if options[:included_files]
puts ui.base.files
elsif options[:interactive]
ui.main
if action == :new || action == :generate_manifest
# do nothing
elsif (arg = ARGV.shift).nil?
gemspecs = Dir['*.gemspec']
gemspec = case gemspecs.length
when 0
nil
when 1
gemspecs.first
else
unless ui.base.build!
puts "If you want to use this options, you need to run"
puts "gemify, set the required options and save it."
end
gemspecs
end
rescue Gemify::UI::EmptyManifest
puts "Can't find anything to make a gem out of..."
rescue Exception => e
puts e.message
elsif File.exists?(arg)
gemspec = arg
else
Trollop.die "No such file: #{arg}"
end

Trollop.die "Extra operand: #{ARGV[0]}" unless ARGV.empty?

cli = Gemify::CLI.load(gemspec)

case action
when :update
puts "Updated manifest (to #{cli.base.files.length} files)"
when :manifest
puts cli.base.inspect_files
when :generate_manifest
puts Gemify::Manifest.files(opts[:generate_manifest])
else
cli.main
end
12 changes: 0 additions & 12 deletions bin/gemify-manifest

This file was deleted.

19 changes: 18 additions & 1 deletion lib/gemify.rb
Expand Up @@ -7,4 +7,21 @@

require 'gemify/base'
require 'gemify/manifest'
require 'gemify/ui'
require 'gemify/cli'

module Gemify
class << self; attr_accessor :last_specification; end
end

# Force Gem::Specification to use Gemify::Base instead.
Gem::Specification.extend Module.new {
def new(*args, &blk)
if self == Gem::Specification
Gemify::Base.new(*args, &blk)
else
Gemify.last_specification = super
end
end
}

Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
170 changes: 30 additions & 140 deletions lib/gemify/base.rb
Expand Up @@ -6,170 +6,60 @@ module Gemify
# == Using
#
# base = Gemify::Base.new(["lib/file.rb", "bin/program"])
# base.settings = {:name => "testing"}
# base[:version] = 0.9
# base.valid? #=> false
# base[:summary] = "A short summary"
# base.valid? #=> true
#
# base.build! # Builds the gem
class Base
attr_accessor :files
attr_reader :settings

class Base < Gem::Specification
REQUIRED = [:name, :summary, :version]
OPTIONAL = [:author, :email, :homepage, :rubyforge_project, :has_rdoc, :dependencies]
ALL = REQUIRED+OPTIONAL
REPLACE = {
:rubyforge_project => "RubyForge project",
:has_rdoc => "documentation",
}
TYPE = {
:has_rdoc => :boolean,
:dependencies => :array,
}
OPTIONAL = [:author, :email, :homepage, :manifest]
ALL = REQUIRED + OPTIONAL

attr_accessor :manifest

def manifest=(type)
@manifest = type && begin
self.files = Gemify::Manifest.files(type)
type
end
end

def initialize(files)
@files = files
@settings = {}
def inspect_files
files.empty? ? "(no files)" : @files.join($/)
end

def settings=(hash)
@settings = hash
ensure_settings!
def version=(version)
@version = version && super
end

# Returns the content of +setting+
def [](setting)
settings[setting]
send(setting) if respond_to?(setting)
end

# Sets the +setting+ to +value+
def []=(setting, value)
if v = cast(setting, value)
settings[setting] = v
else
settings.delete(setting)
end
value = nil if value.respond_to?(:empty?) && value.empty?
send("#{setting}=", value) if respond_to?("#{setting}=")
end

# Returns all the binaries in the file list
def binaries
files.select { |file| file =~ /^bin\/[^\/]+$/ }
end

# Returns all the extensions in the file list
def extensions
files.select { |file| file =~ /extconf\.rb$/ }
def to_ruby
super +
if @manifest
"\nGemify.last_specification.manifest = #{ruby_code manifest} if defined?(Gemify)\n"
else
""
end
end

# Checks if all the required field are set
def valid?
REQUIRED.each do |req|
return false unless settings.keys.include?(req)
end
validate
rescue Gem::InvalidSpecificationException
false
else
true
end

# Returns the type of a setting, defaults to :string
#
# type(:has_rdoc) #=> :boolean
# type(:version) #=> :string
# type(:dependecies) #=> :array
def type(setting)
TYPE[setting] || :string
end

# Returns the name of a setting.
#
# name(:has_rdoc) #=> "documentation"
# name(:name) #=> "name"
# name(:rubyforge_project) #=> "RubyForge project"
def name(setting)
REPLACE[setting] || setting.to_s
end

# Casts +value+ to the right type according to +setting+
#
# Returns nil if +value+ is empty, false or nil.
#
# cast(:has_rdoc, "VICTORY!") #=> true
# cast(:dependencies, "merb-core") #=> ["merb-core"]
# cast(:version, 0.2) #=> "0.2"
#
# cast(:name, "") #=> nil
# cast(:has_rdoc, false) #=> nil
# cast(:dependencies, []) #=> nil
def cast(setting, value)
return nil unless ALL.include?(setting)
case type(setting)
when :array
i = value.to_a
i unless i.empty?
when :string
i = value.to_s
i unless i.empty?
when :boolean
true if !!value
end
end

# Shows the content of +setting+ as a String.
#
# show(:has_rdoc) #=> "true"
# show(:version) #=> "0.9"
# show(:dependencies) #=> "rubygems, merb-core"
# show(:author) #=> nil
def show(setting)
i = settings[setting]
case type(setting)
when :array
i.join(", ")
when :boolean
(!!i).to_s
when :string
i
end
end

# Creates a Gem::Specification+ based on +@files+ and +@settings+
def specification
ensure_settings!
se = settings.clone
Gem::Specification.new do |s|
(se.delete(:dependencies)||[]).each do |dep|
s.add_dependency(dep)
end

se.each { |key, value| s.send("#{key}=",value) }
s.platform = Gem::Platform::RUBY
s.files = files
s.bindir = "bin"
s.require_path = "lib"

unless binaries.empty?
s.executables = binaries.map{|x|x[4..-1]}
end

unless extensions.empty?
s.extensions = extensions
end
end
end

# Builds the gem if it's valid
def build!
# We need to load the specification before valid?
# in order to ensure_settings!
spec = specification
valid? && Gem::Builder.new(spec).build
end

# Ensures that all settings are properly cast.
def ensure_settings!
settings.each do |key, value|
self[key] = value
end
end
end
end

0 comments on commit fb8f016

Please sign in to comment.