diff --git a/lib/rubygems/resources.rb b/lib/rubygems/resources.rb new file mode 100644 index 000000000000..b7c6374960d9 --- /dev/null +++ b/lib/rubygems/resources.rb @@ -0,0 +1,136 @@ +## +# Track Internet resources related to a gem. + +class Gem::Resources + include Enumerable + + ## + # Valid URL (TODO: improve) + + URL = /^(http|https|ftp|mailto)\:\/\/.*?$/ + + ## + # Specail accessor stores values in a hash and can also handle hash + # key +aliases+. + + def self.attr_accessor(name, *aliases) + list = ([name] + aliases).map{ |k| k.to_sym } + list.each do |label| + #list << list.shift until list.first == name + define_method(label) do + key = list.find{ |k| @table[k] } || label + @table[key] + end + define_method("#{label}=") do |url| + raise ArgumentError unless URL =~ url + key = list.find{ |k| @table[k] } || label + @table[key] = url + end + end + end + + ## + # Create new Resources instance. A Hash of label => url + # can be passed as +resources+. + + def initialize(resources={}) + @table = {} + resources.each do |label,url| + add_resource(label, url) + end + end + + ## + # Add a new resource. + + def add_resource(label, url) + __send__("#{label}=", url) + end + + ## + # Convert Resource to a Hash. + + def to_h + @table.inject({}) do |h,(k,v)| + h[k.to_s] = v; h + end + end + + ## + # Project homepage. + + attr_accessor :home, :homepage + + ## + # Source code browser. + + attr_accessor :code, :source, :source_code + + ## + # Issue tracker. + + attr_accessor :bugs, :bug_tracker, :issues, :issue_tracker + + ## + # Mialing list. + + attr_accessor :mail, :mailing_list + + ## + # Documentation. + + attr_accessor :docs, :documentation + + ## + # Wiki pages. + + attr_accessor :wiki + + ## + # Support arbitrary resource labels. + + def method_missing(label, *args) + case label.to_s + when /=$/ + url = args.first + raise ArgumentError unless URL =~ url + label = label.to_s.chomp('=').to_sym + @table[label] = url + else + @table[label] + end + end + + ## + # A resources's hash is the hash of the underlying table. + + def hash # :nodoc: + @table.hash + end + + ## + # Equality. + + def ==(other) + self.class == other.class and @table == other.table + end + + ## + # Iterate over each label, url pair. + + def each(&block) + @table.each(&block) + end + + ## + # Returns the number of reources. + + def size + @table.size + end + + protected + + attr :table + +end diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 0cd569220f1e..5150e99047b7 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -7,6 +7,7 @@ require 'rubygems/version' require 'rubygems/requirement' require 'rubygems/platform' +require 'rubygems/resources' # :stopdoc: class Date; end # for ruby_code if date.rb wasn't required @@ -278,7 +279,8 @@ def _dump(limit) @homepage, @has_rdoc, @new_platform, - @licenses + @licenses, + @resources ] end @@ -324,6 +326,7 @@ def self._load(str) spec.instance_variable_set :@new_platform, array[16] spec.instance_variable_set :@platform, array[16].to_s spec.instance_variable_set :@license, array[17] + spec.instance_variable_set :@resources, array[18] spec.instance_variable_set :@loaded, false spec @@ -764,6 +767,7 @@ def to_ruby handled = [ :dependencies, + :resources, :name, :platform, :required_rubygems_version, @@ -782,6 +786,14 @@ def to_ruby end end + result << nil + + unless resources.empty? then + resources.each do |k,v| + result << " s.add_resource(#{k.inspect}, #{v.inspect})" + end + end + result << nil result << " if s.respond_to? :specification_version then" result << " s.specification_version = #{specification_version}" @@ -1039,6 +1051,15 @@ def add_dependency_with_type(dependency, type, *requirements) private :add_dependency_with_type + ## + # Adds a resource to the gem with given +label+ and +url+. + # Some labels are synonymous with others, e.g. +mail+ with + # +mailing_list+. See Resources class for details. + + def add_resource(label, url) + resources.add_resource(label, url) + end + ## # Finds all gems that satisfy +dep+ @@ -1065,6 +1086,7 @@ def ruby_code(obj) when true, false, nil then obj.inspect when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})" when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})" + when Gem::Resources then "Gem::Resources.new(#{obj.to_h.inspect})" else raise Gem::Exception, "ruby_code case not handled: #{obj.class}" end end @@ -1349,6 +1371,17 @@ def ruby_code(obj) read_only :dependencies + ## + # :attr_reader: resources + # + # Resources instance which stores a mapping of label to URL. + # + # Use #resource or #add_resource to add a resource to a gem. + + attribute :resources, Gem::Resources.new + + read_only :resources + # :section: Aliased gemspec attributes ## diff --git a/test/test_gem_resources.rb b/test/test_gem_resources.rb new file mode 100644 index 000000000000..a8a1d542cbda --- /dev/null +++ b/test/test_gem_resources.rb @@ -0,0 +1,44 @@ +require File.expand_path('../gemutilities', __FILE__) +require 'rubygems/resources' + +class TestGemSpecification < RubyGemTestCase + + def test_resource_new_empty + Gem::Resources.new + end + + def test_resource_new_with_entries + Gem::Resources.new(:wiki=>'http://someplace.org') + end + + def test_add_resource + r = Gem::Resources.new + r.add_resource('home', 'http://someplace.org') + assert_equal 'http://someplace.org', r.home + assert_equal 'http://someplace.org', r.homepage + end + + def test_add_arbitrary_resource + r = Gem::Resources.new + r.add_resource('foo', 'http://someplace.org') + assert_equal 'http://someplace.org', r.foo + end + + def test_to_h + r = Gem::Resources.new + r.add_resource('home', 'http://somehome.org') + r.add_resource(:wiki, 'http://somewiki.org') + h = {'home'=>'http://somehome.org', + 'wiki'=>'http://somewiki.org'} + assert_equal h, r.to_h + end + + def test_aliases + r = Gem::Resources.new + r.add_resource('home', 'http://somehome.org') + assert_equal r.homepage, 'http://somehome.org' + r.add_resource('homepage', 'http://someother.org') + assert_equal r.home, 'http://someother.org' + end + +end diff --git a/test/test_gem_specification.rb b/test/test_gem_specification.rb index 1350bec6365f..4d10e3825e1c 100644 --- a/test/test_gem_specification.rb +++ b/test/test_gem_specification.rb @@ -53,6 +53,8 @@ def setup s.add_dependency 'jabber4r', '> 0.0.0' s.add_dependency 'pqa', ['> 0.4', '<= 0.6'] + s.add_resource 'wiki', 'http://somewiki.org' + s.mark_version s.files = %w[lib/code.rb] end @@ -95,6 +97,7 @@ def test_self_attribute_names required_ruby_version required_rubygems_version requirements + resources rubyforge_project rubygems_version signing_key @@ -365,6 +368,11 @@ def test_dependencies assert_equal [rake, jabber, pqa], @a1.dependencies end + def test_resources + r = Gem::Resources.new 'wiki' => 'http://somewiki.org' + assert_equal r, @a1.resources + end + def test_dependencies_scoped_by_type gem = quick_gem "awesome", "1.0" do |awesome| awesome.add_runtime_dependency "bonobo", [] @@ -776,6 +784,8 @@ def test_to_ruby s.rubygems_version = %q{#{Gem::VERSION}} s.summary = %q{this is a summary} + s.add_resource(:wiki, "http://somewiki.org") + if s.respond_to? :specification_version then s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION} @@ -830,6 +840,8 @@ def test_to_ruby_fancy s.summary = %q{this is a summary} s.test_files = [\"test/suite.rb\"] + s.add_resource(:wiki, "http://somewiki.org") + if s.respond_to? :specification_version then s.specification_version = 3