Class | Gem::Specification |
In: |
lib/rubygems/specification.rb
|
Parent: | Object |
The Specification class contains the metadata for a Gem. Typically defined in a .gemspec file or a Rakefile, and looks like this:
spec = Gem::Specification.new do |s| s.name = 'example' s.version = '1.0' s.summary = 'Example gem specification' ... end
For a great way to package gems, use Hoe.
NONEXISTENT_SPECIFICATION_VERSION | = | -1 | The the version number of a specification that does not specify one (i.e. RubyGems 0.7 or earlier). | |
CURRENT_SPECIFICATION_VERSION | = | 3 | The specification version applied to any new Specification instances created. This should be bumped whenever something in the spec format changes. | |
SPECIFICATION_VERSION_HISTORY | = | { -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'], 1 => [ 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"', '"test_file=x" is a shortcut for "test_files=[x]"' | An informal list of changes to the specification. The highest-valued key should be equal to the CURRENT_SPECIFICATION_VERSION. |
loaded | -> | loaded? |
True if this gem was loaded from disk | ||
== | -> | eql? |
Load custom marshal format, re-initializing defaults as needed
# File lib/rubygems/specification.rb, line 290 290: def self._load(str) 291: array = Marshal.load str 292: 293: spec = Gem::Specification.new 294: spec.instance_variable_set :@specification_version, array[1] 295: 296: current_version = CURRENT_SPECIFICATION_VERSION 297: 298: field_count = if spec.specification_version > current_version then 299: spec.instance_variable_set :@specification_version, 300: current_version 301: MARSHAL_FIELDS[current_version] 302: else 303: MARSHAL_FIELDS[spec.specification_version] 304: end 305: 306: if array.size < field_count then 307: raise TypeError, "invalid Gem::Specification format #{array.inspect}" 308: end 309: 310: spec.instance_variable_set :@rubygems_version, array[0] 311: # spec version 312: spec.instance_variable_set :@name, array[2] 313: spec.instance_variable_set :@version, array[3] 314: spec.instance_variable_set :@date, array[4] 315: spec.instance_variable_set :@summary, array[5] 316: spec.instance_variable_set :@required_ruby_version, array[6] 317: spec.instance_variable_set :@required_rubygems_version, array[7] 318: spec.instance_variable_set :@original_platform, array[8] 319: spec.instance_variable_set :@dependencies, array[9] 320: spec.instance_variable_set :@rubyforge_project, array[10] 321: spec.instance_variable_set :@email, array[11] 322: spec.instance_variable_set :@authors, array[12] 323: spec.instance_variable_set :@description, array[13] 324: spec.instance_variable_set :@homepage, array[14] 325: spec.instance_variable_set :@has_rdoc, array[15] 326: spec.instance_variable_set :@new_platform, array[16] 327: spec.instance_variable_set :@platform, array[16].to_s 328: spec.instance_variable_set :@license, array[17] 329: spec.instance_variable_set :@loaded, false 330: 331: spec 332: end
Same as :attribute, but ensures that values assigned to the attribute are array values by applying :to_a to the value.
# File lib/rubygems/specification.rb, line 177 177: def self.array_attribute(name) 178: @@non_nil_attributes << ["@#{name}".intern, []] 179: 180: @@array_attributes << name 181: @@attributes << [name, []] 182: @@default_value[name] = [] 183: code = %{ 184: def #{name} 185: @#{name} ||= [] 186: end 187: def #{name}=(value) 188: @#{name} = Array(value) 189: end 190: } 191: 192: module_eval code, __FILE__, __LINE__ - 9 193: end
Specification attributes that are arrays (appendable and so-forth)
# File lib/rubygems/specification.rb, line 150 150: def self.array_attributes 151: @@array_attributes.dup 152: end
Specifies the name and default for a specification attribute, and creates a reader and writer method like Module#attr_accessor.
The reader method returns the default if the value hasn‘t been set.
# File lib/rubygems/specification.rb, line 160 160: def self.attribute(name, default=nil) 161: ivar_name = "@#{name}".intern 162: if default.nil? then 163: @@nil_attributes << ivar_name 164: else 165: @@non_nil_attributes << [ivar_name, default] 166: end 167: 168: @@attributes << [name, default] 169: @@default_value[name] = default 170: attr_accessor(name) 171: end
Defines a singular version of an existing plural attribute (i.e. one whose value is expected to be an array). This means just creating a helper method that takes a single value and appends it to the array. These are created for convenience, so that in a spec, one can write
s.require_path = 'mylib'
instead of:
s.require_paths = ['mylib']
That above convenience is available courtesy of:
attribute_alias_singular :require_path, :require_paths
# File lib/rubygems/specification.rb, line 249 249: def self.attribute_alias_singular(singular, plural) 250: define_method("#{singular}=") { |val| 251: send("#{plural}=", [val]) 252: } 253: define_method("#{singular}") { 254: val = send("#{plural}") 255: val.nil? ? nil : val.first 256: } 257: end
Default values for specification attributes
# File lib/rubygems/specification.rb, line 122 122: def self.attribute_defaults 123: @@attributes.dup 124: end
Names of all specification attributes
# File lib/rubygems/specification.rb, line 115 115: def self.attribute_names 116: @@attributes.map { |name, default| name } 117: end
Shortcut for creating several attributes at once (each with a default value of nil).
# File lib/rubygems/specification.rb, line 218 218: def self.attributes(*args) 219: args.each do |arg| 220: attribute(arg, nil) 221: end 222: end
Special loader for YAML files. When a Specification object is loaded from a YAML file, it bypasses the normal Ruby object initialization routine (initialize). This method makes up for that and deals with gems of different ages.
‘input’ can be anything that YAML.load() accepts: String or IO.
# File lib/rubygems/specification.rb, line 480 480: def self.from_yaml(input) 481: input = normalize_yaml_input input 482: spec = YAML.load input 483: 484: if spec && spec.class == FalseClass then 485: raise Gem::EndOfYAMLException 486: end 487: 488: unless Gem::Specification === spec then 489: raise Gem::Exception, "YAML data doesn't evaluate to gem specification" 490: end 491: 492: unless (spec.instance_variables.include? '@specification_version' or 493: spec.instance_variables.include? :@specification_version) and 494: spec.instance_variable_get :@specification_version 495: spec.instance_variable_set :@specification_version, 496: NONEXISTENT_SPECIFICATION_VERSION 497: end 498: 499: spec 500: end
Loads ruby format gemspec from filename
# File lib/rubygems/specification.rb, line 505 505: def self.load(filename) 506: gemspec = nil 507: raise "NESTED Specification.load calls not allowed!" if @@gather 508: @@gather = proc { |gs| gemspec = gs } 509: data = File.read filename 510: eval data, nil, filename 511: gemspec 512: ensure 513: @@gather = nil 514: end
Specification constructor. Assigns the default values to the attributes and yields itself for further initialization. Optionally takes name and version.
# File lib/rubygems/specification.rb, line 418 418: def initialize name = nil, version = nil 419: @new_platform = nil 420: assign_defaults 421: @loaded = false 422: @loaded_from = nil 423: 424: self.name = name if name 425: self.version = version if version 426: 427: yield self if block_given? 428: 429: @@gather.call(self) if @@gather 430: end
Make sure the YAML specification is properly formatted with dashes
# File lib/rubygems/specification.rb, line 519 519: def self.normalize_yaml_input(input) 520: result = input.respond_to?(:read) ? input.read : input 521: result = "--- " + result unless result =~ /^--- / 522: result 523: end
Some attributes require special behaviour when they are accessed. This allows for that.
# File lib/rubygems/specification.rb, line 228 228: def self.overwrite_accessor(name, &block) 229: remove_method name 230: define_method(name, &block) 231: end
Is name a required attribute?
# File lib/rubygems/specification.rb, line 143 143: def self.required_attribute?(name) 144: @@required_attributes.include? name.to_sym 145: end
Required specification attributes
# File lib/rubygems/specification.rb, line 136 136: def self.required_attributes 137: @@required_attributes.dup 138: end
Dump only crucial instance variables.
# File lib/rubygems/specification.rb, line 264 264: def _dump(limit) 265: Marshal.dump [ 266: @rubygems_version, 267: @specification_version, 268: @name, 269: @version, 270: (Time === @date ? @date : (require 'time'; Time.parse(@date.to_s))), 271: @summary, 272: @required_ruby_version, 273: @required_rubygems_version, 274: @original_platform, 275: @dependencies, 276: @rubyforge_project, 277: @email, 278: @authors, 279: @description, 280: @homepage, 281: @has_rdoc, 282: @new_platform, 283: @licenses 284: ] 285: end
Returns an array with bindir attached to each executable in the executables list
# File lib/rubygems/specification.rb, line 374 374: def add_bindir(executables) 375: return nil if executables.nil? 376: 377: if @bindir then 378: Array(executables).map { |e| File.join(@bindir, e) } 379: else 380: executables 381: end 382: rescue 383: return nil 384: end
Adds a development dependency named gem with requirements to this Gem. For example:
spec.add_development_dependency 'jabber4r', '> 0.1', '<= 0.5'
Development dependencies aren‘t installed by default and aren‘t activated when a gem is required.
# File lib/rubygems/specification.rb, line 553 553: def add_development_dependency(gem, *requirements) 554: add_dependency_with_type(gem, :development, *requirements) 555: end
Adds a runtime dependency named gem with requirements to this Gem. For example:
spec.add_runtime_dependency 'jabber4r', '> 0.1', '<= 0.5'
# File lib/rubygems/specification.rb, line 563 563: def add_runtime_dependency(gem, *requirements) 564: add_dependency_with_type(gem, :runtime, *requirements) 565: end
Each attribute has a default value (possibly nil). Here, we initialize all attributes to their default value. This is done through the accessor methods, so special behaviours will be honored. Furthermore, we take a copy of the default so each specification instance has its own empty arrays, etc.
# File lib/rubygems/specification.rb, line 454 454: def assign_defaults 455: @@nil_attributes.each do |name| 456: instance_variable_set name, nil 457: end 458: 459: @@non_nil_attributes.each do |name, default| 460: value = case default 461: when Time, Numeric, Symbol, true, false, nil then default 462: else default.dup 463: end 464: 465: instance_variable_set name, value 466: end 467: 468: # HACK 469: instance_variable_set :@new_platform, Gem::Platform::RUBY 470: end
Return a list of all gems that have a dependency on this gemspec. The list is structured with entries that conform to:
[depending_gem, dependency, [list_of_gems_that_satisfy_dependency]]
# File lib/rubygems/specification.rb, line 956 956: def dependent_gems 957: out = [] 958: Gem.source_index.each do |name,gem| 959: gem.dependencies.each do |dep| 960: if self.satisfies_requirement?(dep) then 961: sats = [] 962: find_all_satisfiers(dep) do |sat| 963: sats << sat 964: end 965: out << [gem, dep, sats] 966: end 967: end 968: end 969: out 970: end
List of dependencies that are used for development
# File lib/rubygems/specification.rb, line 344 344: def development_dependencies 345: dependencies.select { |d| d.type == :development } 346: end
The full path to the gem (install path + full name).
# File lib/rubygems/specification.rb, line 600 600: def full_gem_path 601: path = File.join installation_path, 'gems', full_name 602: return path if File.directory? path 603: File.join installation_path, 'gems', original_name 604: end
Returns the full name (name-version) of this Gem. Platform information is included (name-version-platform) if it is specified and not the default Ruby platform.
# File lib/rubygems/specification.rb, line 577 577: def full_name 578: if platform == Gem::Platform::RUBY or platform.nil? then 579: "#{@name}-#{@version}" 580: else 581: "#{@name}-#{@version}-#{platform}" 582: end 583: end
True if this gem has files in test_files
# File lib/rubygems/specification.rb, line 405 405: def has_unit_tests? 406: not test_files.empty? 407: end
Duplicates array_attributes from other_spec so state isn‘t shared.
# File lib/rubygems/specification.rb, line 435 435: def initialize_copy(other_spec) 436: other_ivars = other_spec.instance_variables 437: other_ivars = other_ivars.map { |ivar| ivar.intern } if # for 1.9 438: other_ivars.any? { |ivar| String === ivar } 439: 440: self.class.array_attributes.each do |name| 441: name = "@#{name}""@#{name}" 442: next unless other_ivars.include? name 443: instance_variable_set name, other_spec.instance_variable_get(name).dup 444: end 445: end
The directory that this gem was installed into.
# File lib/rubygems/specification.rb, line 618 618: def installation_path 619: unless @loaded_from then 620: raise Gem::Exception, "spec #{full_name} is not from an installed gem" 621: end 622: 623: File.expand_path File.dirname(File.dirname(@loaded_from)) 624: end
Sets the rubygems_version to the current RubyGems version
# File lib/rubygems/specification.rb, line 528 528: def mark_version 529: @rubygems_version = Gem::VERSION 530: end
Normalize the list of files so that:
# File lib/rubygems/specification.rb, line 941 941: def normalize 942: if defined?(@extra_rdoc_files) and @extra_rdoc_files then 943: @extra_rdoc_files.uniq! 944: @files ||= [] 945: @files.concat(@extra_rdoc_files) 946: end 947: @files.uniq! if @files 948: end
List of depedencies that will automatically be activated at runtime.
# File lib/rubygems/specification.rb, line 337 337: def runtime_dependencies 338: dependencies.select { |d| d.type == :runtime || d.type == nil } 339: end
Checks if this specification meets the requirement of dependency.
# File lib/rubygems/specification.rb, line 629 629: def satisfies_requirement?(dependency) 630: return @name == dependency.name && 631: dependency.requirement.satisfied_by?(@version) 632: end
Returns an object you can use to sort specifications in sort_by.
# File lib/rubygems/specification.rb, line 637 637: def sort_obj 638: [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1] 639: end
Returns a Ruby code representation of this specification, such that it can be eval‘ed and reconstruct the same specification later. Attributes that still have their default values are omitted.
# File lib/rubygems/specification.rb, line 733 733: def to_ruby 734: mark_version 735: result = [] 736: result << "# -*- encoding: utf-8 -*-" 737: result << nil 738: result << "Gem::Specification.new do |s|" 739: 740: result << " s.name = #{ruby_code name}" 741: result << " s.version = #{ruby_code version}" 742: unless platform.nil? or platform == Gem::Platform::RUBY then 743: result << " s.platform = #{ruby_code original_platform}" 744: end 745: result << "" 746: result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" 747: 748: handled = [ 749: :dependencies, 750: :name, 751: :platform, 752: :required_rubygems_version, 753: :specification_version, 754: :version, 755: ] 756: 757: attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s } 758: 759: attributes.each do |attr_name, default| 760: next if handled.include? attr_name 761: current_value = self.send(attr_name) 762: if current_value != default or 763: self.class.required_attribute? attr_name then 764: result << " s.#{attr_name} = #{ruby_code current_value}" 765: end 766: end 767: 768: result << nil 769: result << " if s.respond_to? :specification_version then" 770: result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION" 771: result << " s.specification_version = #{specification_version}" 772: result << nil 773: 774: result << " if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then" 775: 776: unless dependencies.empty? then 777: dependencies.each do |dep| 778: version_reqs_param = dep.requirements_list.inspect 779: dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK 780: result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})" 781: end 782: end 783: 784: result << " else" 785: 786: unless dependencies.empty? then 787: dependencies.each do |dep| 788: version_reqs_param = dep.requirements_list.inspect 789: result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" 790: end 791: end 792: 793: result << ' end' 794: 795: result << " else" 796: dependencies.each do |dep| 797: version_reqs_param = dep.requirements_list.inspect 798: result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" 799: end 800: result << " end" 801: 802: result << "end" 803: result << nil 804: 805: result.join "\n" 806: end
Checks that the specification contains all required fields, and does a very basic sanity check.
Raises InvalidSpecificationException if the spec does not pass the checks..
# File lib/rubygems/specification.rb, line 815 815: def validate 816: extend Gem::UserInteraction 817: normalize 818: 819: if rubygems_version != Gem::VERSION then 820: raise Gem::InvalidSpecificationException, 821: "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}" 822: end 823: 824: @@required_attributes.each do |symbol| 825: unless self.send symbol then 826: raise Gem::InvalidSpecificationException, 827: "missing value for attribute #{symbol}" 828: end 829: end 830: 831: unless String === name then 832: raise Gem::InvalidSpecificationException, 833: "invalid value for attribute name: \"#{name.inspect}\"" 834: end 835: 836: if require_paths.empty? then 837: raise Gem::InvalidSpecificationException, 838: 'specification must have at least one require_path' 839: end 840: 841: @files.delete_if do |file| File.directory? file end 842: @test_files.delete_if do |file| File.directory? file end 843: @executables.delete_if do |file| 844: File.directory? File.join(bindir, file) 845: end 846: @extra_rdoc_files.delete_if do |file| File.directory? file end 847: @extensions.delete_if do |file| File.directory? file end 848: 849: non_files = files.select do |file| 850: !File.file? file 851: end 852: 853: unless non_files.empty? then 854: non_files = non_files.map { |file| file.inspect } 855: raise Gem::InvalidSpecificationException, 856: "[#{non_files.join ", "}] are not files" 857: end 858: 859: unless specification_version.is_a?(Fixnum) 860: raise Gem::InvalidSpecificationException, 861: 'specification_version must be a Fixnum (did you mean version?)' 862: end 863: 864: case platform 865: when Gem::Platform, Gem::Platform::RUBY then # ok 866: else 867: raise Gem::InvalidSpecificationException, 868: "invalid platform #{platform.inspect}, see Gem::Platform" 869: end 870: 871: unless Array === authors and 872: authors.all? { |author| String === author } then 873: raise Gem::InvalidSpecificationException, 874: 'authors must be Array of Strings' 875: end 876: 877: licenses.each { |license| 878: if license.length > 64 879: raise Gem::InvalidSpecificationException, 880: "each license must be 64 characters or less" 881: end 882: } 883: 884: # reject FIXME and TODO 885: 886: unless authors.grep(/FIXME|TODO/).empty? then 887: raise Gem::InvalidSpecificationException, 888: '"FIXME" or "TODO" is not an author' 889: end 890: 891: unless Array(email).grep(/FIXME|TODO/).empty? then 892: raise Gem::InvalidSpecificationException, 893: '"FIXME" or "TODO" is not an email address' 894: end 895: 896: if description =~ /FIXME|TODO/ then 897: raise Gem::InvalidSpecificationException, 898: '"FIXME" or "TODO" is not a description' 899: end 900: 901: if summary =~ /FIXME|TODO/ then 902: raise Gem::InvalidSpecificationException, 903: '"FIXME" or "TODO" is not a summary' 904: end 905: 906: if homepage and not homepage.empty? and 907: homepage !~ /\A[a-z][a-z\d+.-]*:/i then 908: raise Gem::InvalidSpecificationException, 909: "\"#{homepage}\" is not a URI" 910: end 911: 912: # Warnings 913: 914: %w[author description email homepage rubyforge_project summary].each do |attribute| 915: value = self.send attribute 916: alert_warning "no #{attribute} specified" if value.nil? or value.empty? 917: end 918: 919: if summary and not summary.empty? and description == summary then 920: alert_warning 'description and summary are identical' 921: end 922: 923: alert_warning "deprecated autorequire specified" if autorequire 924: 925: executables.each do |executable| 926: executable_path = File.join bindir, executable 927: shebang = File.read(executable_path, 2) == '#!' 928: 929: alert_warning "#{executable_path} is missing #! line" unless shebang 930: end 931: 932: true 933: end