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.

Constants

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.

External Aliases

loaded -> loaded?
  True if this gem was loaded from disk
== -> eql?

Attributes

loaded  [RW]  true when this gemspec has been loaded from a specifications directory. This attribute is not persisted.
loaded_from  [RW]  Path this gemspec was loaded from. This attribute is not persisted.

Public Class methods

Load custom marshal format, re-initializing defaults as needed

[Source]

     # 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.

[Source]

     # 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)

[Source]

     # 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.

[Source]

     # 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

[Source]

     # 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

[Source]

     # File lib/rubygems/specification.rb, line 122
122:   def self.attribute_defaults
123:     @@attributes.dup
124:   end

Names of all specification attributes

[Source]

     # 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).

[Source]

     # 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

The default value for specification attribute name

[Source]

     # File lib/rubygems/specification.rb, line 129
129:   def self.default_value(name)
130:     @@default_value[name]
131:   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.

[Source]

     # 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 file.

[Source]

     # File lib/rubygems/specification.rb, line 505
505:   def self.load file
506:     return unless file && File.file?(file)
507: 
508:     file = file.dup.untaint
509: 
510:     code = if defined? Encoding
511:              File.read file, :encoding => "UTF-8"
512:            else
513:              File.read file
514:            end
515: 
516:     code.untaint
517: 
518:     begin
519:       spec = eval code, binding, file
520: 
521:       if Gem::Specification === spec
522:         spec.loaded_from = file
523:         return spec
524:       end
525: 
526:       warn "[#{file}] isn't a Gem::Specification (#{spec.class} instead)."
527:     rescue SignalException, SystemExit
528:       raise
529:     rescue SyntaxError, Exception => e
530:       warn "Invalid gemspec in [#{file}]: #{e}"
531:     end
532: 
533:     nil
534:   end

Specification constructor. Assigns the default values to the attributes and yields itself for further initialization. Optionally takes name and version.

[Source]

     # 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

[Source]

     # File lib/rubygems/specification.rb, line 539
539:   def self.normalize_yaml_input(input)
540:     result = input.respond_to?(:read) ? input.read : input
541:     result = "--- " + result unless result =~ /\A--- /
542:     result.gsub(/ !!null \n/, " \n")
543:   end

Some attributes require special behaviour when they are accessed. This allows for that.

[Source]

     # 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

Sometimes we don‘t want the world to use a setter method for a particular attribute.

read_only makes it private so we can still use it internally.

[Source]

     # File lib/rubygems/specification.rb, line 209
209:   def self.read_only(*names)
210:     names.each do |name|
211:       private "#{name}="
212:     end
213:   end

Same as attribute above, but also records this attribute as mandatory.

[Source]

     # File lib/rubygems/specification.rb, line 198
198:   def self.required_attribute(*args)
199:     @@required_attributes << args.first
200:     attribute(*args)
201:   end

Is name a required attribute?

[Source]

     # 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

[Source]

     # File lib/rubygems/specification.rb, line 136
136:   def self.required_attributes
137:     @@required_attributes.dup
138:   end

Public Instance methods

Dump only crucial instance variables.

[Source]

     # 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

[Source]

     # 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
add_dependency(gem, *requirements)

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.

[Source]

     # File lib/rubygems/specification.rb, line 573
573:   def add_development_dependency(gem, *requirements)
574:     add_dependency_with_type(gem, :development, *requirements)
575:   end

Adds a runtime dependency named gem with requirements to this Gem. For example:

  spec.add_runtime_dependency 'jabber4r', '> 0.1', '<= 0.5'

[Source]

     # File lib/rubygems/specification.rb, line 583
583:   def add_runtime_dependency(gem, *requirements)
584:     add_dependency_with_type(gem, :runtime, *requirements)
585:   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.

[Source]

     # 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]]

[Source]

     # File lib/rubygems/specification.rb, line 977
977:   def dependent_gems
978:     out = []
979:     Gem.source_index.each do |name,gem|
980:       gem.dependencies.each do |dep|
981:         if self.satisfies_requirement?(dep) then
982:           sats = []
983:           find_all_satisfiers(dep) do |sat|
984:             sats << sat
985:           end
986:           out << [gem, dep, sats]
987:         end
988:       end
989:     end
990:     out
991:   end

List of dependencies that are used for development

[Source]

     # File lib/rubygems/specification.rb, line 344
344:   def development_dependencies
345:     dependencies.select { |d| d.type == :development }
346:   end

The default (generated) file name of the gem. See also spec_name.

  spec.file_name # => "example-1.0.gem"

[Source]

     # File lib/rubygems/specification.rb, line 631
631:   def file_name
632:     full_name + '.gem'
633:   end

The full path to the gem (install path + full name).

[Source]

     # File lib/rubygems/specification.rb, line 620
620:   def full_gem_path
621:     path = File.join installation_path, 'gems', full_name
622:     return path if File.directory? path
623:     File.join installation_path, 'gems', original_name
624:   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.

[Source]

     # File lib/rubygems/specification.rb, line 597
597:   def full_name
598:     if platform == Gem::Platform::RUBY or platform.nil? then
599:       "#{@name}-#{@version}"
600:     else
601:       "#{@name}-#{@version}-#{platform}"
602:     end
603:   end

True if this gem has files in test_files

[Source]

     # 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.

[Source]

     # 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.

[Source]

     # File lib/rubygems/specification.rb, line 638
638:   def installation_path
639:     unless @loaded_from then
640:       raise Gem::Exception, "spec #{full_name} is not from an installed gem"
641:     end
642: 
643:     File.expand_path File.dirname(File.dirname(@loaded_from))
644:   end

Files in the Gem under one of the require_paths

[Source]

     # File lib/rubygems/specification.rb, line 389
389:   def lib_files
390:     @files.select do |file|
391:       require_paths.any? do |path|
392:         file.index(path) == 0
393:       end
394:     end
395:   end

Sets the rubygems_version to the current RubyGems version

[Source]

     # File lib/rubygems/specification.rb, line 548
548:   def mark_version
549:     @rubygems_version = Gem::VERSION
550:   end

Normalize the list of files so that:

  • All file lists have redundancies removed.
  • Files referenced in the extra_rdoc_files are included in the package file list.

[Source]

     # File lib/rubygems/specification.rb, line 962
962:   def normalize
963:     if defined?(@extra_rdoc_files) and @extra_rdoc_files then
964:       @extra_rdoc_files.uniq!
965:       @files ||= []
966:       @files.concat(@extra_rdoc_files)
967:     end
968:     @files.uniq! if @files
969:   end

List of dependencies that will automatically be activated at runtime.

[Source]

     # 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.

[Source]

     # File lib/rubygems/specification.rb, line 649
649:   def satisfies_requirement?(dependency)
650:     return @name == dependency.name &&
651:       dependency.requirement.satisfied_by?(@version)
652:   end

Returns an object you can use to sort specifications in sort_by.

[Source]

     # File lib/rubygems/specification.rb, line 657
657:   def sort_obj
658:     [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
659:   end

The default name of the gemspec. See also file_name

  spec.spec_name # => "example-1.0.gemspec"

[Source]

     # File lib/rubygems/specification.rb, line 666
666:   def spec_name
667:     full_name + '.gemspec'
668:   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.

[Source]

     # File lib/rubygems/specification.rb, line 754
754:   def to_ruby
755:     mark_version
756:     result = []
757:     result << "# -*- encoding: utf-8 -*-"
758:     result << nil
759:     result << "Gem::Specification.new do |s|"
760: 
761:     result << "  s.name = #{ruby_code name}"
762:     result << "  s.version = #{ruby_code version}"
763:     unless platform.nil? or platform == Gem::Platform::RUBY then
764:       result << "  s.platform = #{ruby_code original_platform}"
765:     end
766:     result << ""
767:     result << "  s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
768: 
769:     handled = [
770:       :dependencies,
771:       :name,
772:       :platform,
773:       :required_rubygems_version,
774:       :specification_version,
775:       :version,
776:     ]
777: 
778:     attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s }
779: 
780:     attributes.each do |attr_name, default|
781:       next if handled.include? attr_name
782:       current_value = self.send(attr_name)
783:       if current_value != default or
784:          self.class.required_attribute? attr_name then
785:         result << "  s.#{attr_name} = #{ruby_code current_value}"
786:       end
787:     end
788: 
789:     result << nil
790:     result << "  if s.respond_to? :specification_version then"
791:     result << "    s.specification_version = #{specification_version}"
792:     result << nil
793: 
794:     result << "    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then"
795: 
796:     unless dependencies.empty? then
797:       dependencies.each do |dep|
798:         version_reqs_param = dep.requirements_list.inspect
799:         dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
800:         result << "      s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})"
801:       end
802:     end
803: 
804:     result << "    else"
805: 
806:     unless dependencies.empty? then
807:       dependencies.each do |dep|
808:         version_reqs_param = dep.requirements_list.inspect
809:         result << "      s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
810:       end
811:     end
812: 
813:     result << '    end'
814: 
815:     result << "  else"
816:       dependencies.each do |dep|
817:         version_reqs_param = dep.requirements_list.inspect
818:         result << "    s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
819:       end
820:     result << "  end"
821: 
822:     result << "end"
823:     result << nil
824: 
825:     result.join "\n"
826:   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..

[Source]

     # File lib/rubygems/specification.rb, line 835
835:   def validate
836:     require 'rubygems/user_interaction'
837:     extend Gem::UserInteraction
838:     normalize
839: 
840:     if rubygems_version != Gem::VERSION then
841:       raise Gem::InvalidSpecificationException,
842:             "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
843:     end
844: 
845:     @@required_attributes.each do |symbol|
846:       unless self.send symbol then
847:         raise Gem::InvalidSpecificationException,
848:               "missing value for attribute #{symbol}"
849:       end
850:     end
851: 
852:     unless String === name then
853:       raise Gem::InvalidSpecificationException,
854:             "invalid value for attribute name: \"#{name.inspect}\""
855:     end
856: 
857:     if require_paths.empty? then
858:       raise Gem::InvalidSpecificationException,
859:             'specification must have at least one require_path'
860:     end
861: 
862:     @files.delete_if            do |file| File.directory? file end
863:     @test_files.delete_if       do |file| File.directory? file end
864:     @executables.delete_if      do |file|
865:       File.directory? File.join(bindir, file)
866:     end
867:     @extra_rdoc_files.delete_if do |file| File.directory? file end
868:     @extensions.delete_if       do |file| File.directory? file end
869: 
870:     non_files = files.select do |file|
871:       !File.file? file
872:     end
873: 
874:     unless non_files.empty? then
875:       non_files = non_files.map { |file| file.inspect }
876:       raise Gem::InvalidSpecificationException,
877:             "[#{non_files.join ", "}] are not files"
878:     end
879: 
880:     unless specification_version.is_a?(Fixnum)
881:       raise Gem::InvalidSpecificationException,
882:             'specification_version must be a Fixnum (did you mean version?)'
883:     end
884: 
885:     case platform
886:     when Gem::Platform, Gem::Platform::RUBY then # ok
887:     else
888:       raise Gem::InvalidSpecificationException,
889:             "invalid platform #{platform.inspect}, see Gem::Platform"
890:     end
891: 
892:     unless Array === authors and
893:            authors.all? { |author| String === author } then
894:       raise Gem::InvalidSpecificationException,
895:             'authors must be Array of Strings'
896:     end
897: 
898:     licenses.each { |license|
899:       if license.length > 64
900:         raise Gem::InvalidSpecificationException,
901:           "each license must be 64 characters or less"
902:       end
903:     }
904: 
905:     # reject FIXME and TODO
906: 
907:     unless authors.grep(/FIXME|TODO/).empty? then
908:       raise Gem::InvalidSpecificationException,
909:             '"FIXME" or "TODO" is not an author'
910:     end
911: 
912:     unless Array(email).grep(/FIXME|TODO/).empty? then
913:       raise Gem::InvalidSpecificationException,
914:             '"FIXME" or "TODO" is not an email address'
915:     end
916: 
917:     if description =~ /FIXME|TODO/ then
918:       raise Gem::InvalidSpecificationException,
919:             '"FIXME" or "TODO" is not a description'
920:     end
921: 
922:     if summary =~ /FIXME|TODO/ then
923:       raise Gem::InvalidSpecificationException,
924:             '"FIXME" or "TODO" is not a summary'
925:     end
926: 
927:     if homepage and not homepage.empty? and
928:        homepage !~ /\A[a-z][a-z\d+.-]*:/i then
929:       raise Gem::InvalidSpecificationException,
930:             "\"#{homepage}\" is not a URI"
931:     end
932: 
933:     # Warnings
934: 
935:     %w[author description email homepage summary].each do |attribute|
936:       value = self.send attribute
937:       alert_warning "no #{attribute} specified" if value.nil? or value.empty?
938:     end
939: 
940:     if summary and not summary.empty? and description == summary then
941:       alert_warning 'description and summary are identical'
942:     end
943: 
944:     alert_warning "deprecated autorequire specified" if autorequire
945: 
946:     executables.each do |executable|
947:       executable_path = File.join bindir, executable
948:       shebang = File.read(executable_path, 2) == '#!'
949: 
950:       alert_warning "#{executable_path} is missing #! line" unless shebang
951:     end
952: 
953:     true
954:   end

Required gemspec attributes

Optional gemspec attributes

External Aliases

has_rdoc -> has_rdoc?
  True if this gem supports RDoc