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.
The the version number of a specification that does not specify one (i.e. RubyGems 0.7 or earlier).
The specification version applied to any new Specification instances created. This should be bumped whenever something in the spec format changes.
An informal list of changes to the specification. The highest-valued key should be equal to the CURRENT_SPECIFICATION_VERSION.
true when this gemspec has been loaded from a specifications directory. This attribute is not persisted.
The rubyforge project this gem lives under. i.e. RubyGems’ rubyforge_project is “rubygems”.
Autorequire was used by old RubyGems to automatically require a file. It no longer is supported.
Deprecated and ignored, defaults to true.
Formerly used to indicate this gem was RDoc-capable.
The license(s) for the library. Each license must be a short name, no more than 64 characters.
Files included in this gem. You cannot append to this accessor, you must assign to it.
Only add files you can require to this list, not directories, etc.
Directories are automatically stripped from this list when building a gem, other non-files cause an error.
Test files included in this gem. You cannot append to this accessor, you must assign to it.
Extensions to build when installing the gem. See Gem::Installer#build_extensions for valid values.
Load custom marshal format, re-initializing defaults as needed
# File lib/rubygems/specification.rb, line 288 def self._load(str) array = Marshal.load str spec = Gem::Specification.new spec.instance_variable_set :@specification_version, array[1] current_version = CURRENT_SPECIFICATION_VERSION field_count = if spec.specification_version > current_version then spec.instance_variable_set :@specification_version, current_version MARSHAL_FIELDS[current_version] else MARSHAL_FIELDS[spec.specification_version] end if array.size < field_count then raise TypeError, "invalid Gem::Specification format #{array.inspect}" end spec.instance_variable_set :@rubygems_version, array[0] # spec version spec.instance_variable_set :@name, array[2] spec.instance_variable_set :@version, array[3] spec.instance_variable_set :@date, array[4] spec.instance_variable_set :@summary, array[5] spec.instance_variable_set :@required_ruby_version, array[6] spec.instance_variable_set :@required_rubygems_version, array[7] spec.instance_variable_set :@original_platform, array[8] spec.instance_variable_set :@dependencies, array[9] spec.instance_variable_set :@rubyforge_project, array[10] spec.instance_variable_set :@email, array[11] spec.instance_variable_set :@authors, array[12] spec.instance_variable_set :@description, array[13] spec.instance_variable_set :@homepage, array[14] spec.instance_variable_set :@has_rdoc, array[15] spec.instance_variable_set :@new_platform, array[16] spec.instance_variable_set :@platform, array[16].to_s spec.instance_variable_set :@license, array[17] spec.instance_variable_set :@loaded, false spec 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 def self.array_attribute(name) @@non_nil_attributes << ["@#{name}".intern, []] @@array_attributes << name @@attributes << [name, []] @@default_value[name] = [] code = %{ def #{name} @#{name} ||= [] end def #{name}=(value) @#{name} = Array(value) end } module_eval code, __FILE__, __LINE__ - 9 end
Specification attributes that are arrays (appendable and so-forth)
# File lib/rubygems/specification.rb, line 148 def self.array_attributes @@array_attributes.dup 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 def self.attribute(name, default=nil) ivar_name = "@#{name}".intern if default.nil? then @@nil_attributes << ivar_name else @@non_nil_attributes << [ivar_name, default] end @@attributes << [name, default] @@default_value[name] = default attr_accessor(name) 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 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
Default values for specification attributes
# File lib/rubygems/specification.rb, line 120 def self.attribute_defaults @@attributes.dup end
Names of all specification attributes
# File lib/rubygems/specification.rb, line 113 def self.attribute_names @@attributes.map { |name, default| name } end
Shortcut for creating several attributes at once (each with a default value of nil).
# File lib/rubygems/specification.rb, line 216 def self.attributes(*args) args.each do |arg| attribute(arg, nil) end end
The default value for specification attribute name
# File lib/rubygems/specification.rb, line 127 def self.default_value(name) @@default_value[name] 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 def self.from_yaml(input) input = normalize_yaml_input input spec = YAML.load input if spec && spec.class == FalseClass then raise Gem::EndOfYAMLException end unless Gem::Specification === spec then raise Gem::Exception, "YAML data doesn't evaluate to gem specification" end unless (spec.instance_variables.include? '@specification_version' or 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
Loads ruby format gemspec from filename
# File lib/rubygems/specification.rb, line 499 def self.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
Specification constructor. Assigns the default values to the attributes and yields itself for further initialization.
# File lib/rubygems/specification.rb, line 415 def initialize @new_platform = nil assign_defaults @loaded = false @loaded_from = nil yield self if block_given? @@gather.call(self) if @@gather end
Make sure the YAML specification is properly formatted with dashes
# File lib/rubygems/specification.rb, line 513 def self.normalize_yaml_input(input) result = input.respond_to?(:read) ? input.read : input result = "--- " + result unless result =~ /^--- / result end
Some attributes require special behaviour when they are accessed. This allows for that.
# File lib/rubygems/specification.rb, line 226 def self.overwrite_accessor(name, &block) remove_method name define_method(name, &block) 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.
# File lib/rubygems/specification.rb, line 207 def self.read_only(*names) names.each do |name| private "#{name}=" end end
Same as attribute above, but also records this attribute as mandatory.
# File lib/rubygems/specification.rb, line 196 def self.required_attribute(*args) @@required_attributes << args.first attribute(*args) end
Dump only crucial instance variables.
# File lib/rubygems/specification.rb, line 262 def _dump(limit) Marshal.dump [ @rubygems_version, @specification_version, @name, @version, (Time === @date ? @date : (require 'time'; Time.parse(@date.to_s))), @summary, @required_ruby_version, @required_rubygems_version, @original_platform, @dependencies, @rubyforge_project, @email, @authors, @description, @homepage, @has_rdoc, @new_platform, @licenses ] end
Returns an array with bindir attached to each executable in the executables list
# File lib/rubygems/specification.rb, line 372 def add_bindir(executables) return nil if executables.nil? if @bindir then Array(executables).map { |e| File.join(@bindir, e) } else executables end rescue return nil 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 def add_development_dependency(gem, *requirements) add_dependency_with_type(gem, :development, *requirements) 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 def add_runtime_dependency(gem, *requirements) add_dependency_with_type(gem, :runtime, *requirements) 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 def assign_defaults @@nil_attributes.each do |name| instance_variable_set name, nil end @@non_nil_attributes.each do |name, default| value = case default when Time, Numeric, Symbol, true, false, nil then default else default.dup end instance_variable_set name, value end # HACK instance_variable_set :@new_platform, Gem::Platform::RUBY 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 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
List of dependencies that are used for development
# File lib/rubygems/specification.rb, line 342 def development_dependencies dependencies.select { |d| d.type == :development } end
Singular accessor for executables
# File lib/rubygems/specification.rb, line 1279 attribute_alias_singular :executable, :executables
The default (generated) file name of the gem.
# File lib/rubygems/specification.rb, line 603 def file_name full_name + ".gem" end
The full path to the gem (install path + full name).
# File lib/rubygems/specification.rb, line 594 def full_gem_path path = File.join installation_path, 'gems', full_name return path if File.directory? path File.join installation_path, 'gems', original_name 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 def full_name if platform == Gem::Platform::RUBY or platform.nil? then "#{@name}-#{@version}" else "#{@name}-#{@version}-#{platform}" end end
has_rdoc is now ignored
# File lib/rubygems/specification.rb, line 1304 overwrite_accessor :has_rdoc do
True if this gem has files in test_files
# File lib/rubygems/specification.rb, line 403 def has_unit_tests? not test_files.empty? end
Duplicates array_attributes from other_spec so state isn’t shared.
# File lib/rubygems/specification.rb, line 429 def initialize_copy(other_spec) other_ivars = other_spec.instance_variables other_ivars = other_ivars.map { |ivar| ivar.intern } if # for 1.9 other_ivars.any? { |ivar| String === ivar } self.class.array_attributes.each do |name| name = :"@#{name}" next unless other_ivars.include? name instance_variable_set name, other_spec.instance_variable_get(name).dup end end
The directory that this gem was installed into.
# File lib/rubygems/specification.rb, line 610 def installation_path unless @loaded_from then raise Gem::Exception, "spec #{full_name} is not from an installed gem" end File.expand_path File.dirname(File.dirname(@loaded_from)) end
Files in the Gem under one of the require_paths
# File lib/rubygems/specification.rb, line 387 def lib_files @files.select do |file| require_paths.any? do |path| file.index(path) == 0 end end end
Singular accessor for licenses
# File lib/rubygems/specification.rb, line 1289 attribute_alias_singular :license, :licenses
Sets the rubygems_version to the current RubyGems version
# File lib/rubygems/specification.rb, line 522 def mark_version @rubygems_version = Gem::RubyGemsVersion end
Normalize the list of files so that:
# File lib/rubygems/specification.rb, line 914 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
Singular accessor for require_paths
# File lib/rubygems/specification.rb, line 1294 attribute_alias_singular :require_path, :require_paths
List of depedencies that will automatically be activated at runtime.
# File lib/rubygems/specification.rb, line 335 def runtime_dependencies dependencies.select { |d| d.type == :runtime || d.type == nil } end
Checks if this specification meets the requirement of dependency.
# File lib/rubygems/specification.rb, line 621 def satisfies_requirement?(dependency) return @name == dependency.name && dependency.version_requirements.satisfied_by?(@version) end
Returns an object you can use to sort specifications in sort_by.
# File lib/rubygems/specification.rb, line 629 def sort_obj [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1] end
Singular accessor for test_files
# File lib/rubygems/specification.rb, line 1299 attribute_alias_singular :test_file, :test_files
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 def to_ruby mark_version result = [] result << "# -*- encoding: utf-8 -*-" result << nil result << "Gem::Specification.new do |s|" result << " s.name = #{ruby_code name}" result << " s.version = #{ruby_code version}" unless platform.nil? or platform == Gem::Platform::RUBY then result << " s.platform = #{ruby_code original_platform}" end result << "" result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" handled = [ :dependencies, :name, :platform, :required_rubygems_version, :specification_version, :version, ] attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s } attributes.each do |attr_name, default| next if handled.include? attr_name current_value = self.send(attr_name) if current_value != default or self.class.required_attribute? attr_name then result << " s.#{attr_name} = #{ruby_code current_value}" end end result << nil result << " if s.respond_to? :specification_version then" result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION" result << " s.specification_version = #{specification_version}" result << nil result << " if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then" unless dependencies.empty? then dependencies.each do |dep| version_reqs_param = dep.requirements_list.inspect dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})" end end result << " else" unless dependencies.empty? then dependencies.each do |dep| version_reqs_param = dep.requirements_list.inspect result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" end end result << ' end' result << " else" dependencies.each do |dep| version_reqs_param = dep.requirements_list.inspect result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" end result << " end" result << "end" result << nil result.join "\n" 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 def validate extend Gem::UserInteraction normalize if rubygems_version != Gem::RubyGemsVersion then raise Gem::InvalidSpecificationException, "expected RubyGems version #{Gem::RubyGemsVersion}, was #{rubygems_version}" end @@required_attributes.each do |symbol| unless self.send symbol then raise Gem::InvalidSpecificationException, "missing value for attribute #{symbol}" end end unless String === name then raise Gem::InvalidSpecificationException, "invalid value for attribute name: \"#{name.inspect}\"" end if require_paths.empty? then raise Gem::InvalidSpecificationException, 'specification must have at least one require_path' end @files.delete_if do |file| File.directory? file end @test_files.delete_if do |file| File.directory? file end @executables.delete_if do |file| File.directory? File.join(bindir, file) end @extra_rdoc_files.delete_if do |file| File.directory? file end @extensions.delete_if do |file| File.directory? file end non_files = files.select do |file| !File.file? file end unless non_files.empty? then non_files = non_files.map { |file| file.inspect } raise Gem::InvalidSpecificationException, "[#{non_files.join ", "}] are not files" end unless specification_version.is_a?(Fixnum) raise Gem::InvalidSpecificationException, 'specification_version must be a Fixnum (did you mean version?)' end case platform when Gem::Platform, Gem::Platform::RUBY then # ok else raise Gem::InvalidSpecificationException, "invalid platform #{platform.inspect}, see Gem::Platform" end unless Array === authors and authors.all? { |author| String === author } then raise Gem::InvalidSpecificationException, 'authors must be Array of Strings' end licenses.each { |license| if license.length > 64 raise Gem::InvalidSpecificationException, "each license must be 64 characters or less" end } # reject FIXME and TODO unless authors.grep(/FIXME|TODO/).empty? then raise Gem::InvalidSpecificationException, '"FIXME" or "TODO" is not an author' end unless Array(email).grep(/FIXME|TODO/).empty? then raise Gem::InvalidSpecificationException, '"FIXME" or "TODO" is not an email address' end if description =~ /FIXME|TODO/ then raise Gem::InvalidSpecificationException, '"FIXME" or "TODO" is not a description' end if summary =~ /FIXME|TODO/ then raise Gem::InvalidSpecificationException, '"FIXME" or "TODO" is not a summary' end if homepage and not homepage.empty? and homepage !~ /\A[a-z][a-z\d+.-]*:/i then raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a URI" end # Warnings %w[author description email homepage rubyforge_project summary].each do |attribute| value = self.send attribute alert_warning "no #{attribute} specified" if value.nil? or value.empty? end if summary and not summary.empty? and description == summary then alert_warning 'description and summary are identical' end alert_warning "deprecated autorequire specified" if autorequire executables.each do |executable| executable_path = File.join bindir, executable shebang = File.read(executable_path, 2) == '#!' alert_warning "#{executable_path} is missing #! line" unless shebang end true end
Adds a dependency on gem dependency with type type that requires requirements. Valid types are currently :runtime and :development.
# File lib/rubygems/specification.rb, line 981 def add_dependency_with_type(dependency, type, *requirements) requirements = if requirements.empty? then Gem::Requirement.default else requirements.flatten end unless dependency.respond_to?(:name) && dependency.respond_to?(:version_requirements) dependency = Gem::Dependency.new(dependency, requirements, type) end dependencies << dependency end
Finds all gems that satisfy dep
# File lib/rubygems/specification.rb, line 1002 def find_all_satisfiers(dep) Gem.source_index.each do |_, gem| yield gem if gem.satisfies_requirement? dep end end
Return a string containing a Ruby code representation of the given object.
# File lib/rubygems/specification.rb, line 1014 def ruby_code(obj) case obj when String then '%q{' + obj + '}' when Array then obj.inspect when Gem::Version then obj.to_s.inspect when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}' when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}' when Numeric then obj.inspect when true, false, nil then obj.inspect when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})" when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})" else raise Gem::Exception, "ruby_code case not handled: #{obj.class}" end end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.