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
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 288 288: def self._load(str) 289: array = Marshal.load str 290: 291: spec = Gem::Specification.new 292: spec.instance_variable_set :@specification_version, array[1] 293: 294: current_version = CURRENT_SPECIFICATION_VERSION 295: 296: field_count = if spec.specification_version > current_version then 297: spec.instance_variable_set :@specification_version, 298: current_version 299: MARSHAL_FIELDS[current_version] 300: else 301: MARSHAL_FIELDS[spec.specification_version] 302: end 303: 304: if array.size < field_count then 305: raise TypeError, "invalid Gem::Specification format #{array.inspect}" 306: end 307: 308: spec.instance_variable_set :@rubygems_version, array[0] 309: # spec version 310: spec.instance_variable_set :@name, array[2] 311: spec.instance_variable_set :@version, array[3] 312: spec.instance_variable_set :@date, array[4] 313: spec.instance_variable_set :@summary, array[5] 314: spec.instance_variable_set :@required_ruby_version, array[6] 315: spec.instance_variable_set :@required_rubygems_version, array[7] 316: spec.instance_variable_set :@original_platform, array[8] 317: spec.instance_variable_set :@dependencies, array[9] 318: spec.instance_variable_set :@rubyforge_project, array[10] 319: spec.instance_variable_set :@email, array[11] 320: spec.instance_variable_set :@authors, array[12] 321: spec.instance_variable_set :@description, array[13] 322: spec.instance_variable_set :@homepage, array[14] 323: spec.instance_variable_set :@has_rdoc, array[15] 324: spec.instance_variable_set :@new_platform, array[16] 325: spec.instance_variable_set :@platform, array[16].to_s 326: spec.instance_variable_set :@license, array[17] 327: spec.instance_variable_set :@loaded, false 328: 329: spec 330: 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 175 175: def self.array_attribute(name) 176: @@non_nil_attributes << ["@#{name}".intern, []] 177: 178: @@array_attributes << name 179: @@attributes << [name, []] 180: @@default_value[name] = [] 181: code = %{ 182: def #{name} 183: @#{name} ||= [] 184: end 185: def #{name}=(value) 186: @#{name} = Array(value) 187: end 188: } 189: 190: module_eval code, __FILE__, __LINE__ - 9 191: end
Specification attributes that are arrays (appendable and so-forth)
# File lib/rubygems/specification.rb, line 148 148: def self.array_attributes 149: @@array_attributes.dup 150: 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 158 158: def self.attribute(name, default=nil) 159: ivar_name = "@#{name}".intern 160: if default.nil? then 161: @@nil_attributes << ivar_name 162: else 163: @@non_nil_attributes << [ivar_name, default] 164: end 165: 166: @@attributes << [name, default] 167: @@default_value[name] = default 168: attr_accessor(name) 169: 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 247 247: def self.attribute_alias_singular(singular, plural) 248: define_method("#{singular}=") { |val| 249: send("#{plural}=", [val]) 250: } 251: define_method("#{singular}") { 252: val = send("#{plural}") 253: val.nil? ? nil : val.first 254: } 255: end
Default values for specification attributes
# File lib/rubygems/specification.rb, line 120 120: def self.attribute_defaults 121: @@attributes.dup 122: end
Names of all specification attributes
# File lib/rubygems/specification.rb, line 113 113: def self.attribute_names 114: @@attributes.map { |name, default| name } 115: end
Shortcut for creating several attributes at once (each with a default value of nil).
# File lib/rubygems/specification.rb, line 216 216: def self.attributes(*args) 217: args.each do |arg| 218: attribute(arg, nil) 219: end 220: 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 474 474: def self.from_yaml(input) 475: input = normalize_yaml_input input 476: spec = YAML.load input 477: 478: if spec && spec.class == FalseClass then 479: raise Gem::EndOfYAMLException 480: end 481: 482: unless Gem::Specification === spec then 483: raise Gem::Exception, "YAML data doesn't evaluate to gem specification" 484: end 485: 486: unless (spec.instance_variables.include? '@specification_version' or 487: spec.instance_variables.include? :@specification_version) and 488: spec.instance_variable_get :@specification_version 489: spec.instance_variable_set :@specification_version, 490: NONEXISTENT_SPECIFICATION_VERSION 491: end 492: 493: spec 494: end
Loads ruby format gemspec from filename
# File lib/rubygems/specification.rb, line 499 499: def self.load(filename) 500: gemspec = nil 501: fail "NESTED Specification.load calls not allowed!" if @@gather 502: @@gather = proc { |gs| gemspec = gs } 503: data = File.read(filename) 504: eval(data) 505: gemspec 506: ensure 507: @@gather = nil 508: end
Specification constructor. Assigns the default values to the attributes and yields itself for further initialization.
# File lib/rubygems/specification.rb, line 415 415: def initialize 416: @new_platform = nil 417: assign_defaults 418: @loaded = false 419: @loaded_from = nil 420: 421: yield self if block_given? 422: 423: @@gather.call(self) if @@gather 424: end
Make sure the YAML specification is properly formatted with dashes
# File lib/rubygems/specification.rb, line 513 513: def self.normalize_yaml_input(input) 514: result = input.respond_to?(:read) ? input.read : input 515: result = "--- " + result unless result =~ /^--- / 516: result 517: end
Some attributes require special behaviour when they are accessed. This allows for that.
# File lib/rubygems/specification.rb, line 226 226: def self.overwrite_accessor(name, &block) 227: remove_method name 228: define_method(name, &block) 229: end
Is name a required attribute?
# File lib/rubygems/specification.rb, line 141 141: def self.required_attribute?(name) 142: @@required_attributes.include? name.to_sym 143: end
Required specification attributes
# File lib/rubygems/specification.rb, line 134 134: def self.required_attributes 135: @@required_attributes.dup 136: end
Dump only crucial instance variables.
# File lib/rubygems/specification.rb, line 262 262: def _dump(limit) 263: Marshal.dump [ 264: @rubygems_version, 265: @specification_version, 266: @name, 267: @version, 268: (Time === @date ? @date : (require 'time'; Time.parse(@date.to_s))), 269: @summary, 270: @required_ruby_version, 271: @required_rubygems_version, 272: @original_platform, 273: @dependencies, 274: @rubyforge_project, 275: @email, 276: @authors, 277: @description, 278: @homepage, 279: @has_rdoc, 280: @new_platform, 281: @licenses 282: ] 283: end
Returns an array with bindir attached to each executable in the executables list
# File lib/rubygems/specification.rb, line 372 372: def add_bindir(executables) 373: return nil if executables.nil? 374: 375: if @bindir then 376: Array(executables).map { |e| File.join(@bindir, e) } 377: else 378: executables 379: end 380: rescue 381: return nil 382: 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 547 547: def add_development_dependency(gem, *requirements) 548: add_dependency_with_type(gem, :development, *requirements) 549: 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 557 557: def add_runtime_dependency(gem, *requirements) 558: add_dependency_with_type(gem, :runtime, *requirements) 559: 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 448 448: def assign_defaults 449: @@nil_attributes.each do |name| 450: instance_variable_set name, nil 451: end 452: 453: @@non_nil_attributes.each do |name, default| 454: value = case default 455: when Time, Numeric, Symbol, true, false, nil then default 456: else default.dup 457: end 458: 459: instance_variable_set name, value 460: end 461: 462: # HACK 463: instance_variable_set :@new_platform, Gem::Platform::RUBY 464: 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 929 929: def dependent_gems 930: out = [] 931: Gem.source_index.each do |name,gem| 932: gem.dependencies.each do |dep| 933: if self.satisfies_requirement?(dep) then 934: sats = [] 935: find_all_satisfiers(dep) do |sat| 936: sats << sat 937: end 938: out << [gem, dep, sats] 939: end 940: end 941: end 942: out 943: end
List of dependencies that are used for development
# File lib/rubygems/specification.rb, line 342 342: def development_dependencies 343: dependencies.select { |d| d.type == :development } 344: end
The default (generated) file name of the gem.
# File lib/rubygems/specification.rb, line 603 603: def file_name 604: full_name + ".gem" 605: end
The full path to the gem (install path + full name).
# File lib/rubygems/specification.rb, line 594 594: def full_gem_path 595: path = File.join installation_path, 'gems', full_name 596: return path if File.directory? path 597: File.join installation_path, 'gems', original_name 598: 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 571 571: def full_name 572: if platform == Gem::Platform::RUBY or platform.nil? then 573: "#{@name}-#{@version}" 574: else 575: "#{@name}-#{@version}-#{platform}" 576: end 577: end
True if this gem has files in test_files
# File lib/rubygems/specification.rb, line 403 403: def has_unit_tests? 404: not test_files.empty? 405: end
Duplicates array_attributes from other_spec so state isn‘t shared.
# File lib/rubygems/specification.rb, line 429 429: def initialize_copy(other_spec) 430: other_ivars = other_spec.instance_variables 431: other_ivars = other_ivars.map { |ivar| ivar.intern } if # for 1.9 432: other_ivars.any? { |ivar| String === ivar } 433: 434: self.class.array_attributes.each do |name| 435: name = "@#{name}""@#{name}" 436: next unless other_ivars.include? name 437: instance_variable_set name, other_spec.instance_variable_get(name).dup 438: end 439: end
The directory that this gem was installed into.
# File lib/rubygems/specification.rb, line 610 610: def installation_path 611: unless @loaded_from then 612: raise Gem::Exception, "spec #{full_name} is not from an installed gem" 613: end 614: 615: File.expand_path File.dirname(File.dirname(@loaded_from)) 616: end
Sets the rubygems_version to the current RubyGems version
# File lib/rubygems/specification.rb, line 522 522: def mark_version 523: @rubygems_version = Gem::RubyGemsVersion 524: end
Normalize the list of files so that:
# File lib/rubygems/specification.rb, line 914 914: def normalize 915: if defined?(@extra_rdoc_files) and @extra_rdoc_files then 916: @extra_rdoc_files.uniq! 917: @files ||= [] 918: @files.concat(@extra_rdoc_files) 919: end 920: @files.uniq! if @files 921: end
List of depedencies that will automatically be activated at runtime.
# File lib/rubygems/specification.rb, line 335 335: def runtime_dependencies 336: dependencies.select { |d| d.type == :runtime || d.type == nil } 337: end
Checks if this specification meets the requirement of dependency.
# File lib/rubygems/specification.rb, line 621 621: def satisfies_requirement?(dependency) 622: return @name == dependency.name && 623: dependency.version_requirements.satisfied_by?(@version) 624: end
Returns an object you can use to sort specifications in sort_by.
# File lib/rubygems/specification.rb, line 629 629: def sort_obj 630: [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1] 631: 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 706 706: def to_ruby 707: mark_version 708: result = [] 709: result << "# -*- encoding: utf-8 -*-" 710: result << nil 711: result << "Gem::Specification.new do |s|" 712: 713: result << " s.name = #{ruby_code name}" 714: result << " s.version = #{ruby_code version}" 715: unless platform.nil? or platform == Gem::Platform::RUBY then 716: result << " s.platform = #{ruby_code original_platform}" 717: end 718: result << "" 719: result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" 720: 721: handled = [ 722: :dependencies, 723: :name, 724: :platform, 725: :required_rubygems_version, 726: :specification_version, 727: :version, 728: ] 729: 730: attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s } 731: 732: attributes.each do |attr_name, default| 733: next if handled.include? attr_name 734: current_value = self.send(attr_name) 735: if current_value != default or 736: self.class.required_attribute? attr_name then 737: result << " s.#{attr_name} = #{ruby_code current_value}" 738: end 739: end 740: 741: result << nil 742: result << " if s.respond_to? :specification_version then" 743: result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION" 744: result << " s.specification_version = #{specification_version}" 745: result << nil 746: 747: result << " if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then" 748: 749: unless dependencies.empty? then 750: dependencies.each do |dep| 751: version_reqs_param = dep.requirements_list.inspect 752: dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK 753: result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})" 754: end 755: end 756: 757: result << " else" 758: 759: unless dependencies.empty? then 760: dependencies.each do |dep| 761: version_reqs_param = dep.requirements_list.inspect 762: result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" 763: end 764: end 765: 766: result << ' end' 767: 768: result << " else" 769: dependencies.each do |dep| 770: version_reqs_param = dep.requirements_list.inspect 771: result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" 772: end 773: result << " end" 774: 775: result << "end" 776: result << nil 777: 778: result.join "\n" 779: 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 788 788: def validate 789: extend Gem::UserInteraction 790: normalize 791: 792: if rubygems_version != Gem::RubyGemsVersion then 793: raise Gem::InvalidSpecificationException, 794: "expected RubyGems version #{Gem::RubyGemsVersion}, was #{rubygems_version}" 795: end 796: 797: @@required_attributes.each do |symbol| 798: unless self.send symbol then 799: raise Gem::InvalidSpecificationException, 800: "missing value for attribute #{symbol}" 801: end 802: end 803: 804: unless String === name then 805: raise Gem::InvalidSpecificationException, 806: "invalid value for attribute name: \"#{name.inspect}\"" 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: @files.delete_if do |file| File.directory? file end 815: @test_files.delete_if do |file| File.directory? file end 816: @executables.delete_if do |file| 817: File.directory? File.join(bindir, file) 818: end 819: @extra_rdoc_files.delete_if do |file| File.directory? file end 820: @extensions.delete_if do |file| File.directory? file end 821: 822: non_files = files.select do |file| 823: !File.file? file 824: end 825: 826: unless non_files.empty? then 827: non_files = non_files.map { |file| file.inspect } 828: raise Gem::InvalidSpecificationException, 829: "[#{non_files.join ", "}] are not files" 830: end 831: 832: unless specification_version.is_a?(Fixnum) 833: raise Gem::InvalidSpecificationException, 834: 'specification_version must be a Fixnum (did you mean version?)' 835: end 836: 837: case platform 838: when Gem::Platform, Gem::Platform::RUBY then # ok 839: else 840: raise Gem::InvalidSpecificationException, 841: "invalid platform #{platform.inspect}, see Gem::Platform" 842: end 843: 844: unless Array === authors and 845: authors.all? { |author| String === author } then 846: raise Gem::InvalidSpecificationException, 847: 'authors must be Array of Strings' 848: end 849: 850: licenses.each{ |license| 851: if license.length > 64 852: raise Gem::InvalidSpecificationException, 853: "each license must be 64 characters or less" 854: end 855: } 856: 857: # reject FIXME and TODO 858: 859: unless authors.grep(/FIXME|TODO/).empty? then 860: raise Gem::InvalidSpecificationException, 861: '"FIXME" or "TODO" is not an author' 862: end 863: 864: unless Array(email).grep(/FIXME|TODO/).empty? then 865: raise Gem::InvalidSpecificationException, 866: '"FIXME" or "TODO" is not an email address' 867: end 868: 869: if description =~ /FIXME|TODO/ then 870: raise Gem::InvalidSpecificationException, 871: '"FIXME" or "TODO" is not a description' 872: end 873: 874: if summary =~ /FIXME|TODO/ then 875: raise Gem::InvalidSpecificationException, 876: '"FIXME" or "TODO" is not a summary' 877: end 878: 879: if homepage and not homepage.empty? and 880: homepage !~ /\A[a-z][a-z\d+.-]*:/i then 881: raise Gem::InvalidSpecificationException, 882: "\"#{homepage}\" is not a URI" 883: end 884: 885: # Warnings 886: 887: %w[author description email homepage rubyforge_project summary].each do |attribute| 888: value = self.send attribute 889: alert_warning "no #{attribute} specified" if value.nil? or value.empty? 890: end 891: 892: if summary and not summary.empty? and description == summary then 893: alert_warning 'description and summary are identical' 894: end 895: 896: alert_warning "deprecated autorequire specified" if autorequire 897: 898: executables.each do |executable| 899: executable_path = File.join bindir, executable 900: shebang = File.read(executable_path, 2) == '#!' 901: 902: alert_warning "#{executable_path} is missing #! line" unless shebang 903: end 904: 905: true 906: end