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 = 'rfoo' s.version = '1.0' s.summary = 'Example gem specification' ... end
There are many gemspec attributes, and the best place to learn about them in the "Gemspec Reference" linked from the RubyGems wiki.
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 | = | 2 | 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 311 311: def self._load(str) 312: array = Marshal.load str 313: 314: spec = Gem::Specification.new 315: spec.instance_variable_set :@specification_version, array[1] 316: 317: current_version = CURRENT_SPECIFICATION_VERSION 318: 319: field_count = if spec.specification_version > current_version then 320: spec.instance_variable_set :@specification_version, 321: current_version 322: MARSHAL_FIELDS[current_version] 323: else 324: MARSHAL_FIELDS[spec.specification_version] 325: end 326: 327: if array.size < field_count then 328: raise TypeError, "invalid Gem::Specification format #{array.inspect}" 329: end 330: 331: spec.instance_variable_set :@rubygems_version, array[0] 332: # spec version 333: spec.instance_variable_set :@name, array[2] 334: spec.instance_variable_set :@version, array[3] 335: spec.instance_variable_set :@date, array[4] 336: spec.instance_variable_set :@summary, array[5] 337: spec.instance_variable_set :@required_ruby_version, array[6] 338: spec.instance_variable_set :@required_rubygems_version, array[7] 339: spec.instance_variable_set :@original_platform, array[8] 340: spec.instance_variable_set :@dependencies, array[9] 341: spec.instance_variable_set :@rubyforge_project, array[10] 342: spec.instance_variable_set :@email, array[11] 343: spec.instance_variable_set :@authors, array[12] 344: spec.instance_variable_set :@description, array[13] 345: spec.instance_variable_set :@homepage, array[14] 346: spec.instance_variable_set :@has_rdoc, array[15] 347: spec.instance_variable_set :@new_platform, array[16] 348: spec.instance_variable_set :@platform, array[16].to_s 349: spec.instance_variable_set :@loaded, false 350: 351: spec 352: 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 199 199: def self.array_attribute(name) 200: @@non_nil_attributes << ["@#{name}".intern, []] 201: 202: @@array_attributes << name 203: @@attributes << [name, []] 204: @@default_value[name] = [] 205: code = %{ 206: def #{name} 207: @#{name} ||= [] 208: end 209: def #{name}=(value) 210: @#{name} = Array(value) 211: end 212: } 213: 214: module_eval code, __FILE__, __LINE__ - 9 215: end
Specification attributes that are arrays (appendable and so-forth)
# File lib/rubygems/specification.rb, line 164 164: def self.array_attributes 165: @@array_attributes.dup 166: 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 182 182: def self.attribute(name, default=nil) 183: ivar_name = "@#{name}".intern 184: if default.nil? then 185: @@nil_attributes << ivar_name 186: else 187: @@non_nil_attributes << [ivar_name, default] 188: end 189: 190: @@attributes << [name, default] 191: @@default_value[name] = default 192: attr_accessor(name) 193: 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 271 271: def self.attribute_alias_singular(singular, plural) 272: define_method("#{singular}=") { |val| 273: send("#{plural}=", [val]) 274: } 275: define_method("#{singular}") { 276: val = send("#{plural}") 277: val.nil? ? nil : val.first 278: } 279: end
Default values for specification attributes
# File lib/rubygems/specification.rb, line 136 136: def self.attribute_defaults 137: @@attributes.dup 138: end
Names of all specification attributes
# File lib/rubygems/specification.rb, line 129 129: def self.attribute_names 130: @@attributes.map { |name, default| name } 131: end
Shortcut for creating several attributes at once (each with a default value of nil).
# File lib/rubygems/specification.rb, line 240 240: def self.attributes(*args) 241: args.each do |arg| 242: attribute(arg, nil) 243: end 244: 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 481 481: def self.from_yaml(input) 482: input = normalize_yaml_input input 483: spec = YAML.load input 484: 485: if spec && spec.class == FalseClass then 486: raise Gem::EndOfYAMLException 487: end 488: 489: unless Gem::Specification === spec then 490: raise Gem::Exception, "YAML data doesn't evaluate to gem specification" 491: end 492: 493: unless (spec.instance_variables.include? '@specification_version' or 494: spec.instance_variables.include? :@specification_version) and 495: spec.instance_variable_get :@specification_version 496: spec.instance_variable_set :@specification_version, 497: NONEXISTENT_SPECIFICATION_VERSION 498: end 499: 500: spec 501: end
A list of Specification instances that have been defined in this Ruby instance.
# File lib/rubygems/specification.rb, line 172 172: def self.list 173: @@list 174: end
Loads ruby format gemspec from filename
# File lib/rubygems/specification.rb, line 506 506: def self.load(filename) 507: gemspec = nil 508: fail "NESTED Specification.load calls not allowed!" if @@gather 509: @@gather = proc { |gs| gemspec = gs } 510: data = File.read(filename) 511: eval(data) 512: gemspec 513: ensure 514: @@gather = nil 515: end
Specification constructor. Assigns the default values to the attributes, adds this spec to the list of loaded specs (see Specification.list), and yields itself for further initialization.
# File lib/rubygems/specification.rb, line 436 436: def initialize 437: @new_platform = nil 438: assign_defaults 439: @loaded = false 440: @loaded_from = nil 441: @@list << self 442: 443: yield self if block_given? 444: 445: @@gather.call(self) if @@gather 446: end
Make sure the YAML specification is properly formatted with dashes
# File lib/rubygems/specification.rb, line 520 520: def self.normalize_yaml_input(input) 521: result = input.respond_to?(:read) ? input.read : input 522: result = "--- " + result unless result =~ /^--- / 523: result 524: end
Some attributes require special behaviour when they are accessed. This allows for that.
# File lib/rubygems/specification.rb, line 250 250: def self.overwrite_accessor(name, &block) 251: remove_method name 252: define_method(name, &block) 253: end
Is name a required attribute?
# File lib/rubygems/specification.rb, line 157 157: def self.required_attribute?(name) 158: @@required_attributes.include? name.to_sym 159: end
Required specification attributes
# File lib/rubygems/specification.rb, line 150 150: def self.required_attributes 151: @@required_attributes.dup 152: end
Dump only crucial instance variables.
# File lib/rubygems/specification.rb, line 286 286: def _dump(limit) 287: Marshal.dump [ 288: @rubygems_version, 289: @specification_version, 290: @name, 291: @version, 292: (Time === @date ? @date : (require 'time'; Time.parse(@date.to_s))), 293: @summary, 294: @required_ruby_version, 295: @required_rubygems_version, 296: @original_platform, 297: @dependencies, 298: @rubyforge_project, 299: @email, 300: @authors, 301: @description, 302: @homepage, 303: @has_rdoc, 304: @new_platform, 305: ] 306: end
Returns an array with bindir attached to each executable in the executables list
# File lib/rubygems/specification.rb, line 394 394: def add_bindir(executables) 395: return nil if executables.nil? 396: 397: if @bindir then 398: Array(executables).map { |e| File.join(@bindir, e) } 399: else 400: executables 401: end 402: rescue 403: return nil 404: 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 554 554: def add_development_dependency(gem, *requirements) 555: add_dependency_with_type(gem, :development, *requirements) 556: 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 564 564: def add_runtime_dependency(gem, *requirements) 565: add_dependency_with_type(gem, :runtime, *requirements) 566: 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 455 455: def assign_defaults 456: @@nil_attributes.each do |name| 457: instance_variable_set name, nil 458: end 459: 460: @@non_nil_attributes.each do |name, default| 461: value = case default 462: when Time, Numeric, Symbol, true, false, nil then default 463: else default.dup 464: end 465: 466: instance_variable_set name, value 467: end 468: 469: # HACK 470: instance_variable_set :@new_platform, Gem::Platform::RUBY 471: 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 872 872: def dependent_gems 873: out = [] 874: Gem.source_index.each do |name,gem| 875: gem.dependencies.each do |dep| 876: if self.satisfies_requirement?(dep) then 877: sats = [] 878: find_all_satisfiers(dep) do |sat| 879: sats << sat 880: end 881: out << [gem, dep, sats] 882: end 883: end 884: end 885: out 886: end
List of dependencies that are used for development
# File lib/rubygems/specification.rb, line 364 364: def development_dependencies 365: dependencies.select { |d| d.type == :development } 366: end
The default (generated) file name of the gem.
# File lib/rubygems/specification.rb, line 610 610: def file_name 611: full_name + ".gem" 612: end
The full path to the gem (install path + full name).
# File lib/rubygems/specification.rb, line 601 601: def full_gem_path 602: path = File.join installation_path, 'gems', full_name 603: return path if File.directory? path 604: File.join installation_path, 'gems', original_name 605: 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 578 578: def full_name 579: if platform == Gem::Platform::RUBY or platform.nil? then 580: "#{@name}-#{@version}" 581: else 582: "#{@name}-#{@version}-#{platform}" 583: end 584: end
True if this gem has files in test_files
# File lib/rubygems/specification.rb, line 425 425: def has_unit_tests? 426: not test_files.empty? 427: end
The directory that this gem was installed into.
# File lib/rubygems/specification.rb, line 617 617: def installation_path 618: path = File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2] 619: path = path.join File::SEPARATOR 620: File.expand_path path 621: end
Sets the rubygems_version to the current RubyGems version
# File lib/rubygems/specification.rb, line 529 529: def mark_version 530: @rubygems_version = RubyGemsVersion 531: end
Normalize the list of files so that:
Also, the summary and description are converted to a normal format.
# File lib/rubygems/specification.rb, line 857 857: def normalize 858: if defined?(@extra_rdoc_files) and @extra_rdoc_files then 859: @extra_rdoc_files.uniq! 860: @files ||= [] 861: @files.concat(@extra_rdoc_files) 862: end 863: @files.uniq! if @files 864: end
List of depedencies that will automatically be activated at runtime.
# File lib/rubygems/specification.rb, line 357 357: def runtime_dependencies 358: dependencies.select { |d| d.type == :runtime || d.type == nil } 359: end
Checks if this specification meets the requirement of dependency.
# File lib/rubygems/specification.rb, line 626 626: def satisfies_requirement?(dependency) 627: return @name == dependency.name && 628: dependency.version_requirements.satisfied_by?(@version) 629: end
Returns an object you can use to sort specifications in sort_by.
# File lib/rubygems/specification.rb, line 634 634: def sort_obj 635: [@name, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1] 636: 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 711 711: def to_ruby 712: mark_version 713: result = [] 714: result << "# -*- encoding: utf-8 -*-" 715: result << nil 716: result << "Gem::Specification.new do |s|" 717: 718: result << " s.name = #{ruby_code name}" 719: result << " s.version = #{ruby_code version}" 720: unless platform.nil? or platform == Gem::Platform::RUBY then 721: result << " s.platform = #{ruby_code original_platform}" 722: end 723: result << "" 724: result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" 725: 726: handled = [ 727: :dependencies, 728: :name, 729: :platform, 730: :required_rubygems_version, 731: :specification_version, 732: :version, 733: ] 734: 735: attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s } 736: 737: attributes.each do |attr_name, default| 738: next if handled.include? attr_name 739: current_value = self.send(attr_name) 740: if current_value != default or 741: self.class.required_attribute? attr_name then 742: result << " s.#{attr_name} = #{ruby_code current_value}" 743: end 744: end 745: 746: result << nil 747: result << " if s.respond_to? :specification_version then" 748: result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION" 749: result << " s.specification_version = #{specification_version}" 750: result << nil 751: 752: result << " if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then" 753: 754: unless dependencies.empty? then 755: dependencies.each do |dep| 756: version_reqs_param = dep.requirements_list.inspect 757: dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK 758: result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})" 759: end 760: end 761: 762: result << " else" 763: 764: unless dependencies.empty? then 765: dependencies.each do |dep| 766: version_reqs_param = dep.requirements_list.inspect 767: result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" 768: end 769: end 770: 771: result << ' end' 772: 773: result << " else" 774: dependencies.each do |dep| 775: version_reqs_param = dep.requirements_list.inspect 776: result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" 777: end 778: result << " end" 779: 780: result << "end" 781: result << nil 782: 783: result.join "\n" 784: end
# File lib/rubygems/specification.rb, line 888 888: def to_s 889: "#<Gem::Specification name=#{@name} version=#{@version}>" 890: 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 793 793: def validate 794: extend Gem::UserInteraction 795: normalize 796: 797: if rubygems_version != RubyGemsVersion then 798: raise Gem::InvalidSpecificationException, 799: "expected RubyGems version #{RubyGemsVersion}, was #{rubygems_version}" 800: end 801: 802: @@required_attributes.each do |symbol| 803: unless self.send symbol then 804: raise Gem::InvalidSpecificationException, 805: "missing value for attribute #{symbol}" 806: end 807: end 808: 809: if require_paths.empty? then 810: raise Gem::InvalidSpecificationException, 811: "specification must have at least one require_path" 812: end 813: 814: case platform 815: when Gem::Platform, Platform::RUBY then # ok 816: else 817: raise Gem::InvalidSpecificationException, 818: "invalid platform #{platform.inspect}, see Gem::Platform" 819: end 820: 821: unless Array === authors and 822: authors.all? { |author| String === author } then 823: raise Gem::InvalidSpecificationException, 824: 'authors must be Array of Strings' 825: end 826: 827: # Warnings 828: 829: %w[author email homepage rubyforge_project summary].each do |attribute| 830: value = self.send attribute 831: alert_warning "no #{attribute} specified" if value.nil? or value.empty? 832: end 833: 834: alert_warning "RDoc will not be generated (has_rdoc == false)" unless 835: has_rdoc 836: 837: alert_warning "deprecated autorequire specified" if autorequire 838: 839: executables.each do |executable| 840: executable_path = File.join bindir, executable 841: shebang = File.read(executable_path, 2) == '#!' 842: 843: alert_warning "#{executable_path} is missing #! line" unless shebang 844: end 845: 846: true 847: end