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.

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

[Source]

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

[Source]

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

[Source]

     # 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

[Source]

     # 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

[Source]

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

Names of all specification attributes

[Source]

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

[Source]

     # 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

The default value for specification attribute name

[Source]

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

[Source]

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

[Source]

     # 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

[Source]

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

[Source]

     # 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

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 207
207:   def self.read_only(*names)
208:     names.each do |name|
209:       private "#{name}="
210:     end
211:   end

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

[Source]

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

Is name a required attribute?

[Source]

     # 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

[Source]

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

Public Instance methods

Dump only crucial instance variables.

[Source]

     # 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

[Source]

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

[Source]

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

[Source]

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

[Source]

     # 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

[Source]

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

[Source]

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

[Source]

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

[Source]

     # 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

[Source]

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

[Source]

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

[Source]

     # 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

Files in the Gem under one of the require_paths

[Source]

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

Sets the rubygems_version to the current RubyGems version

[Source]

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

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

[Source]

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

[Source]

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

[Source]

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

[Source]

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

[Source]

     # 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

Required gemspec attributes

Optional gemspec attributes

External Aliases

has_rdoc -> has_rdoc?
  True if this gem supports RDoc