Class: Gem::Specification

Inherits:
Object
  • Object
show all
Defined in:
lib/rubygems/specification.rb

Overview

Gem::Specification

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.

Constant Summary collapse

NONEXISTENT_SPECIFICATION_VERSION =

The the version number of a specification that does not specify one (i.e. RubyGems 0.7 or earlier).

-1
CURRENT_SPECIFICATION_VERSION =

The specification version applied to any new Specification instances created. This should be bumped whenever something in the spec format changes.

1
SPECIFICATION_VERSION_HISTORY =

An informal list of changes to the specification. The highest-valued key should be equal to the CURRENT_SPECIFICATION_VERSION.

{
  -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]"'
  ]
}
@@list =

List of Specification instances.

[]
@@gather =

Optional block used to gather newly defined instances.

nil
@@required_attributes =

List of attribute names: [:name, :version, …]

[]
@@attributes =

List of all attributes and default values: [[:name, nil], [:bindir, ‘bin’], …]

[]
@@array_attributes =

List of array attributes

[]
@@default_value =

Map of attribute names to default values.

{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize {|_self| ... } ⇒ Specification

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.

Yields:

  • (_self)

Yield Parameters:



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/rubygems/specification.rb', line 406

def initialize
  # 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.
  @@attributes.each do |name, default|
    if RUBY_VERSION >= "1.9" then
      self.funcall "#{name}=", copy_of(default)
    else
      self.send "#{name}=", copy_of(default)
    end
  end
  @loaded = false
  @@list << self
  yield self if block_given?
  @@gather.call(self) if @@gather
end

Instance Attribute Details

#loaded=(value) ⇒ Object (writeonly)

RUNTIME attributes (not persisted) —————————–



282
283
284
# File 'lib/rubygems/specification.rb', line 282

def loaded=(value)
  @loaded = value
end

#loaded_fromObject

Returns the value of attribute loaded_from.



283
284
285
# File 'lib/rubygems/specification.rb', line 283

def loaded_from
  @loaded_from
end

Class Method Details

.array_attribute(name) ⇒ Object

Same as :attribute, but ensures that values assigned to the attribute are array values by applying :to_a to the value.



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/rubygems/specification.rb', line 149

def self.array_attribute(name)
  @@array_attributes << name
  @@attributes << [name, []]
  @@default_value[name] = []
  module_eval %{
    def #{name}
      @#{name} ||= []
    end
    def #{name}=(value)
      @#{name} = value.to_a
    end
  }
end

.array_attributesObject



115
116
117
# File 'lib/rubygems/specification.rb', line 115

def self.array_attributes
  @@array_attributes.dup
end

.attribute(name, default = nil) ⇒ Object

Used to specify the name and default value of a specification attribute. The side effects are:

  • the name and default value are added to the @@attributes list and @@default_value map

  • a standard writer method (attribute=) is created

  • a non-standard _reader method (attribute) is created

The reader method behaves like this:

def attribute
  @attribute ||= (copy of default value)
end

This allows lazy initialization of attributes to their default values.



141
142
143
144
145
# File 'lib/rubygems/specification.rb', line 141

def self.attribute(name, default=nil)
  @@attributes << [name, default]
  @@default_value[name] = default
  attr_accessor(name)
end

.attribute_alias_singular(singular, plural) ⇒ Object

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


208
209
210
211
212
213
214
215
216
# File 'lib/rubygems/specification.rb', line 208

def self.attribute_alias_singular(singular, plural)
  define_method("#{singular}=") { |val|
    send("#{plural}=", [val])
  }
  define_method("#{singular}") { 
    val = send("#{plural}")
    val.nil? ? nil : val.first
  }
end

.attribute_defaultsObject



99
100
101
# File 'lib/rubygems/specification.rb', line 99

def self.attribute_defaults
  @@attributes.dup
end

.attribute_namesObject

————————- Convenience class methods.



95
96
97
# File 'lib/rubygems/specification.rb', line 95

def self.attribute_names
  @@attributes.map { |name, default| name }
end

.attributes(*args) ⇒ Object

Shortcut for creating several attributes at once (each with a default value of nil).



179
180
181
182
183
# File 'lib/rubygems/specification.rb', line 179

def self.attributes(*args)
  args.each do |arg|
    attribute(arg, nil)
  end
end

.default_value(name) ⇒ Object



103
104
105
# File 'lib/rubygems/specification.rb', line 103

def self.default_value(name)
  @@default_value[name]
end

.from_yaml(input) ⇒ Object

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.



432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/rubygems/specification.rb', line 432

def Specification.from_yaml(input)
  input = normalize_yaml_input(input)
  spec = YAML.load(input)
  if(spec && spec.class == FalseClass) then
    raise Gem::EndOfYAMLException
  end
  unless Specification === spec
    raise Gem::Exception, "YAML data doesn't evaluate to gem specification"
  end
  unless spec.instance_variables.include? '@specification_version' and
         spec.instance_variable_get :@specification_version
    spec.instance_variable_set :@specification_version, 
      NONEXISTENT_SPECIFICATION_VERSION
  end
  spec
end

.listObject

A list of Specification instances that have been defined in this Ruby instance.



122
123
124
# File 'lib/rubygems/specification.rb', line 122

def self.list
  @@list
end

.load(filename) ⇒ Object



449
450
451
452
453
454
455
456
457
458
# File 'lib/rubygems/specification.rb', line 449

def Specification.load(filename)
  gemspec = nil
  fail "NESTED Specification.load calls not allowed!" if @@gather
  @@gather = proc { |gs| gemspec = gs }
  data = File.read(filename)
  eval(data)
  gemspec
ensure
  @@gather = nil
end

.normalize_yaml_input(input) ⇒ Object

Make sure the yaml specification is properly formatted with dashes.



461
462
463
464
465
# File 'lib/rubygems/specification.rb', line 461

def Specification.normalize_yaml_input(input)
  result = input.respond_to?(:read) ? input.read : input
  result = "--- " + result unless result =~ /^--- /
  result
end

.overwrite_accessor(name, &block) ⇒ Object

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



187
188
189
190
# File 'lib/rubygems/specification.rb', line 187

def self.overwrite_accessor(name, &block)
  remove_method name
  define_method(name, &block)
end

.read_only(*names) ⇒ Object

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.



171
172
173
174
175
# File 'lib/rubygems/specification.rb', line 171

def self.read_only(*names)
  names.each do |name|
    private "#{name}="
  end
end

.required_attribute(*args) ⇒ Object

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



164
165
166
167
# File 'lib/rubygems/specification.rb', line 164

def self.required_attribute(*args)
  @@required_attributes << args.first
  attribute(*args)
end

.required_attribute?(name) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/rubygems/specification.rb', line 111

def self.required_attribute?(name)
  @@required_attributes.include? name.to_sym
end

.required_attributesObject



107
108
109
# File 'lib/rubygems/specification.rb', line 107

def self.required_attributes
  @@required_attributes.dup
end

Instance Method Details

#<=>(other) ⇒ Object

Compare specs (name then version).



534
535
536
# File 'lib/rubygems/specification.rb', line 534

def <=>(other)
  [@name, @version] <=> [other.name, other.version]
end

#==(other) ⇒ Object Also known as: eql?

Tests specs for equality (across all attributes).



539
540
541
# File 'lib/rubygems/specification.rb', line 539

def ==(other) # :nodoc:
  other.kind_of?(self.class) && same_attributes?(other)
end

#add_bindir(executables) ⇒ Object



354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/rubygems/specification.rb', line 354

def add_bindir(executables)
  if not defined? @executables || @executables.nil?
    return nil
  end

  if defined? @bindir and @bindir then
    @executables.map {|e| File.join(@bindir, e) }
  else
    @executables
  end
rescue
  return nil
end

#add_dependency(gem, *requirements) ⇒ Object

Adds a dependency to this Gem. For example,

spec.add_dependency('jabber4r', '> 0.1', '<= 0.5')
gem
String or Gem::Dependency

The Gem name/dependency.

requirements
default=“> 0.0.0”

The version requirements.



482
483
484
485
486
487
488
489
# File 'lib/rubygems/specification.rb', line 482

def add_dependency(gem, *requirements)
  requirements = ['> 0.0.0'] if requirements.empty?
  requirements.flatten!
  unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements)
    gem = Dependency.new(gem, requirements)
  end
  dependencies << gem
end

#dependent_gemsObject

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]]
return
Array
[dependent_gem, dependency, [list_of_satisfiers]]


639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
# File 'lib/rubygems/specification.rb', line 639

def dependent_gems
  out = []
  Gem.source_index.each do |name,gem|
    gem.dependencies.each do |dep|
      if self.satisfies_requirement?(dep) then
        sats = []
        find_all_satisfiers(dep) do |sat|
          sats << sat
        end
        out << [gem, dep, sats]
      end
    end
  end
  out
end

#full_gem_pathObject

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

return
String

the full gem path



507
508
509
# File 'lib/rubygems/specification.rb', line 507

def full_gem_path
  File.join(installation_path, "gems", full_name)
end

#full_nameObject

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



495
496
497
498
499
500
501
# File 'lib/rubygems/specification.rb', line 495

def full_name
  if platform == Gem::Platform::RUBY || platform.nil?
    "#{@name}-#{@version}"
  else
    "#{@name}-#{@version}-#{platform}"
  end 
end

#has_rdoc?Boolean

Returns:

  • (Boolean)


396
# File 'lib/rubygems/specification.rb', line 396

def has_rdoc?; has_rdoc ? true : false ; end

#has_unit_tests?Boolean Also known as: has_test_suite?

Returns:

  • (Boolean)


397
# File 'lib/rubygems/specification.rb', line 397

def has_unit_tests?; not test_files.empty?; end

#hashObject

:nodoc:



553
554
555
556
557
558
# File 'lib/rubygems/specification.rb', line 553

def hash # :nodoc:
  @@attributes.inject(0) { |hash_code, (name, default_value)|
    n = self.send(name).hash
    hash_code + n
  }
end

#installation_pathObject

The root directory that the gem was installed into.

return
String

the installation path



515
516
517
518
# File 'lib/rubygems/specification.rb', line 515

def installation_path
  (File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]).
    join(File::SEPARATOR)
end

#loaded?Boolean

Predicates —————————————————–

Returns:

  • (Boolean)


395
# File 'lib/rubygems/specification.rb', line 395

def loaded?; @loaded ? true : false ; end

#mark_versionObject

Sets the rubygems_version to Gem::RubyGemsVersion.



471
472
473
# File 'lib/rubygems/specification.rb', line 471

def mark_version
  @rubygems_version = RubyGemsVersion
end

#normalizeObject

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.

Also, the summary and description are converted to a normal format.



621
622
623
624
625
626
627
628
# File 'lib/rubygems/specification.rb', line 621

def normalize
  if defined? @extra_rdoc_files and @extra_rdoc_files then
    @extra_rdoc_files.uniq!
    @files ||= []
    @files.concat(@extra_rdoc_files)
  end
  @files.uniq! if @files
end

#satisfies_requirement?(dependency) ⇒ Boolean

Checks if this Specification meets the requirement of the supplied dependency.

dependency
Gem::Dependency

the dependency to check

return
Boolean

true if dependency is met, otherwise false

Returns:

  • (Boolean)


526
527
528
529
# File 'lib/rubygems/specification.rb', line 526

def satisfies_requirement?(dependency)
  return @name == dependency.name && 
    dependency.version_requirements.satisfied_by?(@version)
end

#test_suite_fileObject

DEPRECATED gemspec attributes ———————————-



269
270
271
272
# File 'lib/rubygems/specification.rb', line 269

def test_suite_file
  warn_deprecated(:test_suite_file, :test_files)
  test_files.first
end

#test_suite_file=(val) ⇒ Object



274
275
276
277
278
# File 'lib/rubygems/specification.rb', line 274

def test_suite_file=(val)
  warn_deprecated(:test_suite_file, :test_files)
  @test_files = [] unless defined? @test_files
  @test_files << val
end

#to_rubyObject

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.



574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# File 'lib/rubygems/specification.rb', line 574

def to_ruby
  mark_version
  result = "Gem::Specification.new do |s|\n"
  @@attributes.each do |name, default|
    # TODO better implementation of next line (read_only_attribute? ... something like that)
    next if name == :dependencies or name == :specification_version
    current_value = self.send(name)
    result << "  s.#{name} = #{ruby_code(current_value)}\n" unless current_value == default
  end
  dependencies.each do |dep|
    version_reqs_param = dep.requirements_list.inspect
    result << "  s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})\n"
  end
  result << "end\n"
end

#to_sObject



655
656
657
# File 'lib/rubygems/specification.rb', line 655

def to_s
  "#<Gem::Specification name=#{@name} version=#{@version}>"
end

#to_yaml_propertiesObject

Returns an array of attribute names to be used when generating a YAML representation of this object. If an attribute still has its default value, it is omitted.



565
566
567
568
# File 'lib/rubygems/specification.rb', line 565

def to_yaml_properties
  mark_version
  @@attributes.map { |name, default| "@#{name}" }
end

#validateObject

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



597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'lib/rubygems/specification.rb', line 597

def validate
  normalize
  if rubygems_version != RubyGemsVersion
    raise InvalidSpecificationException.new(%[
      Expected RubyGems Version #{RubyGemsVersion}, was #{rubygems_version}
    ].strip)
  end
  @@required_attributes.each do |symbol|
    unless self.send(symbol)
      raise InvalidSpecificationException.new("Missing value for attribute #{symbol}")
    end
  end 
  if require_paths.empty?
    raise InvalidSpecificationException.new("Gem spec needs to have at least one require_path")
  end
end

#warn_deprecated(old, new) ⇒ Object



218
219
220
221
# File 'lib/rubygems/specification.rb', line 218

def warn_deprecated(old, new)
  # How (if at all) to implement this?  We only want to warn when
  # a gem is being built, I should think.
end