Файловый менеджер - Редактировать - /home/fresvfqn/crimescenecleaningupsuffolkcounty.com/wp-includes/widgets/14338/share.tar
Назад
systemtap/tapset/libruby.so.3.0.stp 0000644 00000020726 15102661023 0013217 0 ustar 00 /* SystemTap tapset to make it easier to trace Ruby 2.0 * * All probes provided by Ruby can be listed using following command * (the path to the library must be adjuste appropriately): * * stap -L 'process("/opt/alt/ruby30/lib*\/libruby.so.3.0").mark("*")' */ /** * probe ruby.array.create - Allocation of new array. * * @size: Number of elements (an int) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.array.create = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("array__create") { size = $arg1 file = user_string($arg2) line = $arg3 } /** * probe ruby.cmethod.entry - Fired just before a method implemented in C is entered. * * @classname: Name of the class (string) * @methodname: The method about bo be executed (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.cmethod.entry = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("cmethod__entry") { classname = user_string($arg1) methodname = user_string($arg2) file = user_string($arg3) line = $arg4 } /** * probe ruby.cmethod.return - Fired just after a method implemented in C has returned. * * @classname: Name of the class (string) * @methodname: The executed method (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.cmethod.return = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("cmethod__return") { classname = user_string($arg1) methodname = user_string($arg2) file = user_string($arg3) line = $arg4 } /** * probe ruby.find.require.entry - Fired when require starts to search load * path for suitable file to require. * * @requiredfile: The name of the file to be required (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.find.require.entry = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("find__require__entry") { requiredfile = user_string($arg1) file = user_string($arg2) line = $arg3 } /** * probe ruby.find.require.return - Fired just after require has finished * search of load path for suitable file to require. * * @requiredfile: The name of the file to be required (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.find.require.return = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("find__require__return") { requiredfile = user_string($arg1) file = user_string($arg2) line = $arg3 } /** * probe ruby.gc.mark.begin - Fired when a GC mark phase is about to start. * * It takes no arguments. */ probe ruby.gc.mark.begin = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("gc__mark__begin") { } /** * probe ruby.gc.mark.end - Fired when a GC mark phase has ended. * * It takes no arguments. */ probe ruby.gc.mark.end = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("gc__mark__end") { } /** * probe ruby.gc.sweep.begin - Fired when a GC sweep phase is about to start. * * It takes no arguments. */ probe ruby.gc.sweep.begin = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("gc__sweep__begin") { } /** * probe ruby.gc.sweep.end - Fired when a GC sweep phase has ended. * * It takes no arguments. */ probe ruby.gc.sweep.end = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("gc__sweep__end") { } /** * probe ruby.hash.create - Allocation of new hash. * * @size: Number of elements (int) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.hash.create = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("hash__create") { size = $arg1 file = user_string($arg2) line = $arg3 } /** * probe ruby.load.entry - Fired when calls to "load" are made. * * @loadedfile: The name of the file to be loaded (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.load.entry = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("load__entry") { loadedfile = user_string($arg1) file = user_string($arg2) line = $arg3 } /** * probe ruby.load.return - Fired just after require has finished * search of load path for suitable file to require. * * @loadedfile: The name of the file that was loaded (string) */ probe ruby.load.return = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("load__return") { loadedfile = user_string($arg1) } /** * probe ruby.method.entry - Fired just before a method implemented in Ruby is entered. * * @classname: Name of the class (string) * @methodname: The method about bo be executed (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.method.entry = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("method__entry") { classname = user_string($arg1) methodname = user_string($arg2) file = user_string($arg3) line = $arg4 } /** * probe ruby.method.return - Fired just after a method implemented in Ruby has returned. * * @classname: Name of the class (string) * @methodname: The executed method (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.method.return = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("method__return") { classname = user_string($arg1) methodname = user_string($arg2) file = user_string($arg3) line = $arg4 } /** * probe ruby.object.create - Allocation of new object. * * @classname: Name of the class (string) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.object.create = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("object__create") { classname = user_string($arg1) file = user_string($arg2) line = $arg3 } /** * probe ruby.parse.begin - Fired just before a Ruby source file is parsed. * * @parsedfile: The name of the file to be parsed (string) * @parsedline: The line number of beginning of parsing (int) */ probe ruby.parse.begin = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("parse__begin") { parsedfile = user_string($arg1) parsedline = $arg2 } /** * probe ruby.parse.end - Fired just after a Ruby source file was parsed. * * @parsedfile: The name of parsed the file (string) * @parsedline: The line number of beginning of parsing (int) */ probe ruby.parse.end = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("parse__end") { parsedfile = user_string($arg1) parsedline = $arg2 } /** * probe ruby.raise - Fired when an exception is raised. * * @classname: The class name of the raised exception (string) * @file: The name of the file where the exception was raised (string) * @line: The line number in the file where the exception was raised (int) */ probe ruby.raise = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("raise") { classname = user_string($arg1) file = user_string($arg2) line = $arg3 } /** * probe ruby.require.entry - Fired on calls to rb_require_safe (when a file * is required). * * @requiredfile: The name of the file to be required (string) * @file: The file that called "require" (string) * @line: The line number where the call to require was made(int) */ probe ruby.require.entry = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("require__entry") { requiredfile = user_string($arg1) file = user_string($arg2) line = $arg3 } /** * probe ruby.require.return - Fired just after require has finished * search of load path for suitable file to require. * * @requiredfile: The file that was required (string) */ probe ruby.require.return = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("require__return") { requiredfile = user_string($arg1) } /** * probe ruby.string.create - Allocation of new string. * * @size: Number of elements (an int) * @file: The file name where the method is being called (string) * @line: The line number where the method is being called (int) */ probe ruby.string.create = process("/opt/alt/ruby30/lib*/libruby.so.3.0").mark("string__create") { size = $arg1 file = user_string($arg2) line = $arg3 } doc/alt-ruby30-libs/README.md 0000644 00000015150 15102661023 0011417 0 ustar 00 [](https://travis-ci.org/ruby/ruby) [](https://ci.appveyor.com/project/ruby/ruby/branch/master) [](https://github.com/ruby/ruby/actions?query=workflow%3A"macOS") [](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW") [](https://github.com/ruby/ruby/actions?query=workflow%3A"MJIT") [](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu") [](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows") # What's Ruby Ruby is an interpreted object-oriented programming language often used for web development. It also offers many scripting features to process plain text and serialized files, or manage system tasks. It is simple, straightforward, and extensible. ## Features of Ruby * Simple Syntax * **Normal** Object-oriented Features (e.g. class, method calls) * **Advanced** Object-oriented Features (e.g. mix-in, singleton-method) * Operator Overloading * Exception Handling * Iterators and Closures * Garbage Collection * Dynamic Loading of Object Files (on some architectures) * Highly Portable (works on many Unix-like/POSIX compatible platforms as well as Windows, macOS, etc.) cf. https://github.com/ruby/ruby/blob/master/doc/contributing.rdoc#label-Platform+Maintainers ## How to get Ruby For a complete list of ways to install Ruby, including using third-party tools like rvm, see: https://www.ruby-lang.org/en/downloads/ ### Git The mirror of the Ruby source tree can be checked out with the following command: $ git clone https://github.com/ruby/ruby.git There are some other branches under development. Try the following command to see the list of branches: $ git ls-remote https://github.com/ruby/ruby.git You may also want to use https://git.ruby-lang.org/ruby.git (actual master of Ruby source) if you are a committer. ### Subversion Stable branches for older Ruby versions can be checked out with also the following command: $ svn co https://svn.ruby-lang.org/repos/ruby/branches/ruby_2_6/ ruby Try the following command to see the list of branches: $ svn ls https://svn.ruby-lang.org/repos/ruby/branches/ ## Ruby home page https://www.ruby-lang.org/ ## Mailing list There is a mailing list to discuss Ruby. To subscribe to this list, please send the following phrase: subscribe in the mail body (not subject) to the address [ruby-talk-request@ruby-lang.org]. [ruby-talk-request@ruby-lang.org]: mailto:ruby-talk-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe ## How to compile and install 1. If you want to use Microsoft Visual C++ to compile Ruby, read [win32/README.win32](win32/README.win32) instead of this document. 2. Run `./autogen.sh` to generate configure, when you build the source checked out from the Git repository. 3. Run `./configure`, which will generate `config.h` and `Makefile`. Some C compiler flags may be added by default depending on your environment. Specify `optflags=..` and `warnflags=..` as necessary to override them. 4. Edit `include/ruby/defines.h` if you need. Usually this step will not be needed. 5. Remove comment mark(`#`) before the module names from `ext/Setup` (or add module names if not present), if you want to link modules statically. If you don't want to compile non static extension modules (probably on architectures which do not allow dynamic loading), remove comment mark from the line "`#option nodynamic`" in `ext/Setup`. Usually this step will not be needed. 6. Run `make`. * On Mac, set RUBY\_CODESIGN environment variable with a signing identity. It uses the identity to sign `ruby` binary. See also codesign(1). 7. Optionally, run '`make check`' to check whether the compiled Ruby interpreter works well. If you see the message "`check succeeded`", your Ruby works as it should (hopefully). 8. Run '`make install`'. This command will create the following directories and install files into them. * `${DESTDIR}${prefix}/bin` * `${DESTDIR}${prefix}/include/ruby-${MAJOR}.${MINOR}.${TEENY}` * `${DESTDIR}${prefix}/include/ruby-${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` * `${DESTDIR}${prefix}/lib` * `${DESTDIR}${prefix}/lib/ruby` * `${DESTDIR}${prefix}/lib/ruby/${MAJOR}.${MINOR}.${TEENY}` * `${DESTDIR}${prefix}/lib/ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` * `${DESTDIR}${prefix}/lib/ruby/site_ruby` * `${DESTDIR}${prefix}/lib/ruby/site_ruby/${MAJOR}.${MINOR}.${TEENY}` * `${DESTDIR}${prefix}/lib/ruby/site_ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby` * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby/${MAJOR}.${MINOR}.${TEENY}` * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}` * `${DESTDIR}${prefix}/lib/ruby/gems/${MAJOR}.${MINOR}.${TEENY}` * `${DESTDIR}${prefix}/share/man/man1` * `${DESTDIR}${prefix}/share/ri/${MAJOR}.${MINOR}.${TEENY}/system` If Ruby's API version is '*x.y.z*', the `${MAJOR}` is '*x*', the `${MINOR}` is '*y*', and the `${TEENY}` is '*z*'. **NOTE**: teeny of the API version may be different from one of Ruby's program version You may have to be a super user to install Ruby. If you fail to compile Ruby, please send the detailed error report with the error log and machine/OS type, to help others. Some extension libraries may not get compiled because of lack of necessary external libraries and/or headers, then you will need to run '`make distclean-ext`' to remove old configuration after installing them in such case. ## Copying See the file [COPYING](COPYING). ## Feedback Questions about the Ruby language can be asked on the Ruby-Talk mailing list (https://www.ruby-lang.org/en/community/mailing-lists) or on websites like (https://stackoverflow.com). Bugs should be reported at https://bugs.ruby-lang.org. Read [HowToReport] for more information. [HowToReport]: https://bugs.ruby-lang.org/projects/ruby/wiki/HowToReport ## Contributing See the file [CONTRIBUTING.md](CONTRIBUTING.md) ## The Author Ruby was originally designed and developed by Yukihiro Matsumoto (Matz) in 1995. <matz@ruby-lang.org> doc/alt-ruby30-libs/NEWS.md 0000644 00000060454 15102661023 0011245 0 ustar 00 # NEWS for Ruby 3.0.0 This document is a list of user visible feature changes since the **2.7.0** release, except for bug fixes. Note that each entry is kept so brief that no reason behind or reference information is supplied with. For a full list of changes with all sufficient information, see the ChangeLog file or Redmine (e.g. `https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER`). ## Language changes * Keyword arguments are now separated from positional arguments. Code that resulted in deprecation warnings in Ruby 2.7 will now result in ArgumentError or different behavior. [[Feature #14183]] * Procs accepting a single rest argument and keywords are no longer subject to autosplatting. This now matches the behavior of Procs accepting a single rest argument and no keywords. [[Feature #16166]] ```ruby pr = proc{|*a, **kw| [a, kw]} pr.call([1]) # 2.7 => [[1], {}] # 3.0 => [[[1]], {}] pr.call([1, {a: 1}]) # 2.7 => [[1], {:a=>1}] # and deprecation warning # 3.0 => [[[1, {:a=>1}]], {}] ``` * Arguments forwarding (`...`) now supports leading arguments. [[Feature #16378]] ```ruby def method_missing(meth, ...) send(:"do_#{meth}", ...) end ``` * Pattern matching (`case/in`) is no longer experimental. [[Feature #17260]] * One-line pattern matching is redesigned. [EXPERIMENTAL] * `=>` is added. It can be used like a rightward assignment. [[Feature #17260]] ```ruby 0 => a p a #=> 0 {b: 0, c: 1} => {b:} p b #=> 0 ``` * `in` is changed to return `true` or `false`. [[Feature #17371]] ```ruby # version 3.0 0 in 1 #=> false # version 2.7 0 in 1 #=> raise NoMatchingPatternError ``` * Find-pattern is added. [EXPERIMENTAL] [[Feature #16828]] ```ruby case ["a", 1, "b", "c", 2, "d", "e", "f", 3] in [*pre, String => x, String => y, *post] p pre #=> ["a", 1] p x #=> "b" p y #=> "c" p post #=> [2, "d", "e", "f", 3] end ``` * Endless method definition is added. [EXPERIMENTAL] [[Feature #16746]] ```ruby def square(x) = x * x ``` * Interpolated String literals are no longer frozen when `# frozen-string-literal: true` is used. [[Feature #17104]] * Magic comment `shareable_constant_value` added to freeze constants. See {Magic Comments}[rdoc-ref:doc/syntax/comments.rdoc@Magic+Comments] for more details. [[Feature #17273]] * A {static analysis}[rdoc-label:label-Static+analysis] foundation is introduced. * {RBS}[rdoc-label:label-RBS] is introduced. It is a type definition language for Ruby programs. * {TypeProf}[rdoc-label:label-TypeProf] is experimentally bundled. It is a type analysis tool for Ruby programs. * Deprecation warnings are no longer shown by default (since Ruby 2.7.2). Turn them on with `-W:deprecated` (or with `-w` to show other warnings too). [[Feature #16345]] * $SAFE and $KCODE are now normal global variables with no special behavior. C-API methods related to $SAFE have been removed. [[Feature #16131]] [[Feature #17136]] * yield in singleton class definitions in methods is now a SyntaxError instead of a warning. yield in a class definition outside of a method is now a SyntaxError instead of a LocalJumpError. [[Feature #15575]] * When a class variable is overtaken by the same definition in an ancestor class/module, a RuntimeError is now raised (previously, it only issued a warning in verbose mode). Additionally, accessing a class variable from the toplevel scope is now a RuntimeError. [[Bug #14541]] * Assigning to a numbered parameter is now a SyntaxError instead of a warning. ## Command line options ### `--help` option When the environment variable `RUBY_PAGER` or `PAGER` is present and has a non-empty value, and the standard input and output are tty, the `--help` option shows the help message via the pager designated by the value. [[Feature #16754]] ### `--backtrace-limit` option The `--backtrace-limit` option limits the maximum length of a backtrace. [[Feature #8661]] ## Core classes updates Outstanding ones only. * Array * The following methods now return Array instances instead of subclass instances when called on subclass instances: [[Bug #6087]] * Array#drop * Array#drop_while * Array#flatten * Array#slice! * Array#slice / Array#[] * Array#take * Array#take_while * Array#uniq * Array#* * Can be sliced with Enumerator::ArithmeticSequence ```ruby dirty_data = ['--', 'data1', '--', 'data2', '--', 'data3'] dirty_data[(1..).step(2)] # take each second element # => ["data1", "data2", "data3"] ``` * Binding * Binding#eval when called with one argument will use "(eval)" for `__FILE__` and 1 for `__LINE__` in the evaluated code. [[Bug #4352]] [[Bug #17419]] * ConditionVariable * ConditionVariable#wait may now invoke the `block`/`unblock` scheduler hooks in a non-blocking context. [[Feature #16786]] * Dir * Dir.glob and Dir.[] now sort the results by default, and accept the `sort:` keyword option. [[Feature #8709]] * ENV * ENV.except has been added, which returns a hash excluding the given keys and their values. [[Feature #15822]] * Windows: Read ENV names and values as UTF-8 encoded Strings [[Feature #12650]] * Encoding * Added new encoding IBM720. [[Feature #16233]] * Changed default for Encoding.default_external to UTF-8 on Windows [[Feature #16604]] * Fiber * Fiber.new(blocking: true/false) allows you to create non-blocking execution contexts. [[Feature #16786]] * Fiber#blocking? tells whether the fiber is non-blocking. [[Feature #16786]] * Fiber#backtrace and Fiber#backtrace_locations provide per-fiber backtrace. [[Feature #16815]] * The limitation of Fiber#transfer is relaxed. [[Bug #17221]] * GC * GC.auto_compact= and GC.auto_compact have been added to control when compaction runs. Setting `auto_compact=` to true will cause compaction to occur during major collections. At the moment, compaction adds significant overhead to major collections, so please test first! [[Feature #17176]] * Hash * Hash#transform_keys and Hash#transform_keys! now accept a hash that maps keys to new keys. [[Feature #16274]] * Hash#except has been added, which returns a hash excluding the given keys and their values. [[Feature #15822]] * IO * IO#nonblock? now defaults to `true`. [[Feature #16786]] * IO#wait_readable, IO#wait_writable, IO#read, IO#write and other related methods (e.g. IO#puts, IO#gets) may invoke the scheduler hook `#io_wait(io, events, timeout)` in a non-blocking execution context. [[Feature #16786]] * Kernel * Kernel#clone when called with the `freeze: false` keyword will call `#initialize_clone` with the `freeze: false` keyword. [[Bug #14266]] * Kernel#clone when called with the `freeze: true` keyword will call `#initialize_clone` with the `freeze: true` keyword, and will return a frozen copy even if the receiver is unfrozen. [[Feature #16175]] * Kernel#eval when called with two arguments will use "(eval)" for `__FILE__` and 1 for `__LINE__` in the evaluated code. [[Bug #4352]] * Kernel#lambda now warns if called without a literal block. [[Feature #15973]] * Kernel.sleep invokes the scheduler hook `#kernel_sleep(...)` in a non-blocking execution context. [[Feature #16786]] * Module * Module#include and Module#prepend now affect classes and modules that have already included or prepended the receiver, mirroring the behavior if the arguments were included in the receiver before the other modules and classes included or prepended the receiver. [[Feature #9573]] ```ruby class C; end module M1; end module M2; end C.include M1 M1.include M2 p C.ancestors #=> [C, M1, M2, Object, Kernel, BasicObject] ``` * Module#public, Module#protected, Module#private, Module#public_class_method, Module#private_class_method, toplevel "private" and "public" methods now accept single array argument with a list of method names. [[Feature #17314]] * Module#attr_accessor, Module#attr_reader, Module#attr_writer and Module#attr methods now return an array of defined method names as symbols. [[Feature #17314]] * Module#alias_method now returns the defined alias as a symbol. [[Feature #17314]] * Mutex * `Mutex` is now acquired per-`Fiber` instead of per-`Thread`. This change should be compatible for essentially all usages and avoids blocking when using a scheduler. [[Feature #16792]] * Proc * Proc#== and Proc#eql? are now defined and will return true for separate Proc instances if the procs were created from the same block. [[Feature #14267]] * Queue / SizedQueue * Queue#pop, SizedQueue#push and related methods may now invoke the `block`/`unblock` scheduler hooks in a non-blocking context. [[Feature #16786]] * Ractor * New class added to enable parallel execution. See rdoc-ref:ractor.md for more details. * Random * `Random::DEFAULT` now refers to the `Random` class instead of being a `Random` instance, so it can work with `Ractor`. [[Feature #17322]] * `Random::DEFAULT` is deprecated since its value is now confusing and it is no longer global, use `Kernel.rand`/`Random.rand` directly, or create a `Random` instance with `Random.new` instead. [[Feature #17351]] * String * The following methods now return or yield String instances instead of subclass instances when called on subclass instances: [[Bug #10845]] * String#* * String#capitalize * String#center * String#chomp * String#chop * String#delete * String#delete_prefix * String#delete_suffix * String#downcase * String#dump * String#each_char * String#each_grapheme_cluster * String#each_line * String#gsub * String#ljust * String#lstrip * String#partition * String#reverse * String#rjust * String#rpartition * String#rstrip * String#scrub * String#slice! * String#slice / String#[] * String#split * String#squeeze * String#strip * String#sub * String#succ / String#next * String#swapcase * String#tr * String#tr_s * String#upcase * Symbol * Symbol#to_proc now returns a lambda Proc. [[Feature #16260]] * Symbol#name has been added, which returns the name of the symbol if it is named. The returned string is frozen. [[Feature #16150]] * Fiber * Introduce Fiber.set_scheduler for intercepting blocking operations and Fiber.scheduler for accessing the current scheduler. See rdoc-ref:fiber.md for more details about what operations are supported and how to implement the scheduler hooks. [[Feature #16786]] * Fiber.blocking? tells whether the current execution context is blocking. [[Feature #16786]] * Thread#join invokes the scheduler hooks `block`/`unblock` in a non-blocking execution context. [[Feature #16786]] * Thread * Thread.ignore_deadlock accessor has been added for disabling the default deadlock detection, allowing the use of signal handlers to break deadlock. [[Bug #13768]] * Warning * Warning#warn now supports a category keyword argument. [[Feature #17122]] ## Stdlib updates Outstanding ones only. * BigDecimal * Update to BigDecimal 3.0.0 * This version is Ractor compatible. * Bundler * Update to Bundler 2.2.3 * CGI * Update to 0.2.0 * This version is Ractor compatible. * CSV * Update to CSV 3.1.9 * Date * Update to Date 3.1.1 * This version is Ractor compatible. * Digest * Update to Digest 3.0.0 * This version is Ractor compatible. * Etc * Update to Etc 1.2.0 * This version is Ractor compatible. * Fiddle * Update to Fiddle 1.0.5 * IRB * Update to IRB 1.2.6 * JSON * Update to JSON 2.5.0 * This version is Ractor compatible. * Set * Update to set 1.0.0 * SortedSet has been removed for dependency and performance reasons. * Set#join is added as a shorthand for `.to_a.join`. * Set#<=> is added. * Socket * Add :connect_timeout to TCPSocket.new [[Feature #17187]] * Net::HTTP * Net::HTTP#verify_hostname= and Net::HTTP#verify_hostname have been added to skip hostname verification. [[Feature #16555]] * Net::HTTP.get, Net::HTTP.get_response, and Net::HTTP.get_print can take the request headers as a Hash in the second argument when the first argument is a URI. [[Feature #16686]] * Net::SMTP * Add SNI support. * Net::SMTP.start arguments are keyword arguments. * TLS should not check the host name by default. * OpenStruct * Initialization is no longer lazy. [[Bug #12136]] * Builtin methods can now be overridden safely. [[Bug #15409]] * Implementation uses only methods ending with `!`. * Ractor compatible. * Improved support for YAML. [[Bug #8382]] * Use officially discouraged. Read OpenStruct@Caveats section. * Pathname * Ractor compatible. * Psych * Update to Psych 3.3.0 * This version is Ractor compatible. * Reline * Update to Reline 0.1.5 * RubyGems * Update to RubyGems 3.2.3 * StringIO * Update to StringIO 3.0.0 * This version is Ractor compatible. * StringScanner * Update to StringScanner 3.0.0 * This version is Ractor compatible. ## Compatibility issues Excluding feature bug fixes. * Regexp literals and all Range objects are frozen. [[Feature #8948]] [[Feature #16377]] [[Feature #15504]] ```ruby /foo/.frozen? #=> true (42...).frozen? # => true ``` * EXPERIMENTAL: Hash#each consistently yields a 2-element array. [[Bug #12706]] * Now `{ a: 1 }.each(&->(k, v) { })` raises an ArgumentError due to lambda's arity check. * When writing to STDOUT redirected to a closed pipe, no broken pipe error message will be shown now. [[Feature #14413]] * `TRUE`/`FALSE`/`NIL` constants are no longer defined. * Integer#zero? overrides Numeric#zero? for optimization. [[Misc #16961]] * Enumerable#grep and Enumerable#grep_v when passed a Regexp and no block no longer modify Regexp.last_match. [[Bug #17030]] * Requiring 'open-uri' no longer redefines `Kernel#open`. Call `URI.open` directly or `use URI#open` instead. [[Misc #15893]] * SortedSet has been removed for dependency and performance reasons. ## Stdlib compatibility issues * Default gems * The following libraries are promoted to default gems from stdlib. * English * abbrev * base64 * drb * debug * erb * find * net-ftp * net-http * net-imap * net-protocol * open-uri * optparse * pp * prettyprint * resolv-replace * resolv * rinda * set * securerandom * shellwords * tempfile * tmpdir * time * tsort * un * weakref * The following extensions are promoted to default gems from stdlib. * digest * io-nonblock * io-wait * nkf * pathname * syslog * win32ole * Bundled gems * net-telnet and xmlrpc have been removed from the bundled gems. If you are interested in maintaining them, please comment on your plan to https://github.com/ruby/xmlrpc or https://github.com/ruby/net-telnet. * SDBM has been removed from the Ruby standard library. [[Bug #8446]] * The issues of sdbm will be handled at https://github.com/ruby/sdbm * WEBrick has been removed from the Ruby standard library. [[Feature #17303]] * The issues of WEBrick will be handled at https://github.com/ruby/webrick ## C API updates * C API functions related to $SAFE have been removed. [[Feature #16131]] * C API header file `ruby/ruby.h` was split. [[GH-2991]] This should have no impact on extension libraries, but users might experience slow compilations. * Memory view interface [EXPERIMENTAL] * The memory view interface is a C-API set to exchange a raw memory area, such as a numeric array or a bitmap image, between extension libraries. The extension libraries can share also the metadata of the memory area that consists of the shape, the element format, and so on. Using these kinds of metadata, the extension libraries can share even a multidimensional array appropriately. This feature is designed by referring to Python's buffer protocol. [[Feature #13767]] [[Feature #14722]] * Ractor related C APIs are introduced (experimental) in "include/ruby/ractor.h". ## Implementation improvements * New method cache mechanism for Ractor. [[Feature #16614]] * Inline method caches pointed from ISeq can be accessed by multiple Ractors in parallel and synchronization is needed even for method caches. However, such synchronization can be overhead so introducing new inline method cache mechanisms, (1) Disposable inline method cache (2) per-Class method cache and (3) new invalidation mechanism. (1) can avoid per-method call synchronization because it only uses atomic operations. See the ticket for more details. * The number of hashes allocated when using a keyword splat in a method call has been reduced to a maximum of 1, and passing a keyword splat to a method that accepts specific keywords does not allocate a hash. * `super` is optimized when the same type of method is called in the previous call if it's not refinements or an attr reader or writer. ### JIT * Performance improvements of JIT-ed code * Microarchitectural optimizations * Native functions shared by multiple methods are deduplicated on JIT compaction. * Decrease code size of hot paths by some optimizations and partitioning cold paths. * Instance variables * Eliminate some redundant checks. * Skip checking a class and a object multiple times in a method when possible. * Optimize accesses in some core classes like Hash and their subclasses. * Method inlining support for some C methods * `Kernel`: `#class`, `#frozen?` * `Integer`: `#-@`, `#~`, `#abs`, `#bit_length`, `#even?`, `#integer?`, `#magnitude`, `#odd?`, `#ord`, `#to_i`, `#to_int`, `#zero?` * `Struct`: reader methods for 10th or later members * Constant references are inlined. * Always generate appropriate code for `==`, `nil?`, and `!` calls depending on a receiver class. * Reduce the number of PC accesses on branches and method returns. * Optimize C method calls a little. * Compilation process improvements * It does not keep temporary files in /tmp anymore. * Throttle GC and compaction of JIT-ed code. * Avoid GC-ing JIT-ed code when not necessary. * GC-ing JIT-ed code is executed in a background thread. * Reduce the number of locks between Ruby and JIT threads. ## Static analysis ### RBS * RBS is a new language for type definition of Ruby programs. It allows writing types of classes and modules with advanced types including union types, overloading, generics, and _interface types_ for duck typing. * Ruby ships with type definitions for core/stdlib classes. * `rbs` gem is bundled to load and process RBS files. ### TypeProf * TypeProf is a type analysis tool for Ruby code based on abstract interpretation. * It reads non-annotated Ruby code, tries inferring its type signature, and prints the analysis result in RBS format. * Though it supports only a subset of the Ruby language yet, we will continuously improve the coverage of language features, analysis performance, and usability. ```ruby # test.rb def foo(x) if x > 10 x.to_s else nil end end foo(42) ``` ``` $ typeprof test.rb # Classes class Object def foo : (Integer) -> String? end ``` ## Miscellaneous changes * Methods using `ruby2_keywords` will no longer keep empty keyword splats, those are now removed just as they are for methods not using `ruby2_keywords`. * When an exception is caught in the default handler, the error message and backtrace are printed in order from the innermost. [[Feature #8661]] * Accessing an uninitialized instance variable no longer emits a warning in verbose mode. [[Feature #17055]] [Bug #4352]: https://bugs.ruby-lang.org/issues/4352 [Bug #6087]: https://bugs.ruby-lang.org/issues/6087 [Bug #8382]: https://bugs.ruby-lang.org/issues/8382 [Bug #8446]: https://bugs.ruby-lang.org/issues/8446 [Feature #8661]: https://bugs.ruby-lang.org/issues/8661 [Feature #8709]: https://bugs.ruby-lang.org/issues/8709 [Feature #8948]: https://bugs.ruby-lang.org/issues/8948 [Feature #9573]: https://bugs.ruby-lang.org/issues/9573 [Bug #10845]: https://bugs.ruby-lang.org/issues/10845 [Bug #12136]: https://bugs.ruby-lang.org/issues/12136 [Feature #12650]: https://bugs.ruby-lang.org/issues/12650 [Bug #12706]: https://bugs.ruby-lang.org/issues/12706 [Feature #13767]: https://bugs.ruby-lang.org/issues/13767 [Bug #13768]: https://bugs.ruby-lang.org/issues/13768 [Feature #14183]: https://bugs.ruby-lang.org/issues/14183 [Bug #14266]: https://bugs.ruby-lang.org/issues/14266 [Feature #14267]: https://bugs.ruby-lang.org/issues/14267 [Feature #14413]: https://bugs.ruby-lang.org/issues/14413 [Bug #14541]: https://bugs.ruby-lang.org/issues/14541 [Feature #14722]: https://bugs.ruby-lang.org/issues/14722 [Bug #15409]: https://bugs.ruby-lang.org/issues/15409 [Feature #15504]: https://bugs.ruby-lang.org/issues/15504 [Feature #15575]: https://bugs.ruby-lang.org/issues/15575 [Feature #15822]: https://bugs.ruby-lang.org/issues/15822 [Misc #15893]: https://bugs.ruby-lang.org/issues/15893 [Feature #15921]: https://bugs.ruby-lang.org/issues/15921 [Feature #15973]: https://bugs.ruby-lang.org/issues/15973 [Feature #16131]: https://bugs.ruby-lang.org/issues/16131 [Feature #16150]: https://bugs.ruby-lang.org/issues/16150 [Feature #16166]: https://bugs.ruby-lang.org/issues/16166 [Feature #16175]: https://bugs.ruby-lang.org/issues/16175 [Feature #16233]: https://bugs.ruby-lang.org/issues/16233 [Feature #16260]: https://bugs.ruby-lang.org/issues/16260 [Feature #16274]: https://bugs.ruby-lang.org/issues/16274 [Feature #16345]: https://bugs.ruby-lang.org/issues/16345 [Feature #16377]: https://bugs.ruby-lang.org/issues/16377 [Feature #16378]: https://bugs.ruby-lang.org/issues/16378 [Feature #16555]: https://bugs.ruby-lang.org/issues/16555 [Feature #16604]: https://bugs.ruby-lang.org/issues/16604 [Feature #16614]: https://bugs.ruby-lang.org/issues/16614 [Feature #16686]: https://bugs.ruby-lang.org/issues/16686 [Feature #16746]: https://bugs.ruby-lang.org/issues/16746 [Feature #16754]: https://bugs.ruby-lang.org/issues/16754 [Feature #16786]: https://bugs.ruby-lang.org/issues/16786 [Feature #16792]: https://bugs.ruby-lang.org/issues/16792 [Feature #16815]: https://bugs.ruby-lang.org/issues/16815 [Feature #16828]: https://bugs.ruby-lang.org/issues/16828 [Misc #16961]: https://bugs.ruby-lang.org/issues/16961 [Bug #17030]: https://bugs.ruby-lang.org/issues/17030 [Feature #17055]: https://bugs.ruby-lang.org/issues/17055 [Feature #17104]: https://bugs.ruby-lang.org/issues/17104 [Feature #17122]: https://bugs.ruby-lang.org/issues/17122 [Feature #17136]: https://bugs.ruby-lang.org/issues/17136 [Feature #17176]: https://bugs.ruby-lang.org/issues/17176 [Feature #17187]: https://bugs.ruby-lang.org/issues/17187 [Bug #17221]: https://bugs.ruby-lang.org/issues/17221 [Feature #17260]: https://bugs.ruby-lang.org/issues/17260 [Feature #17273]: https://bugs.ruby-lang.org/issues/17273 [Feature #17303]: https://bugs.ruby-lang.org/issues/17303 [Feature #17314]: https://bugs.ruby-lang.org/issues/17314 [Feature #17322]: https://bugs.ruby-lang.org/issues/17322 [Feature #17351]: https://bugs.ruby-lang.org/issues/17351 [Feature #17371]: https://bugs.ruby-lang.org/issues/17371 [Bug #17419]: https://bugs.ruby-lang.org/issues/17419 [GH-2991]: https://github.com/ruby/ruby/pull/2991 rubygems/rubygems.rb 0000644 00000111573 15102661023 0010567 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require 'rbconfig' module Gem VERSION = "3.2.33".freeze end # Must be first since it unloads the prelude from 1.9.2 require_relative 'rubygems/compatibility' require_relative 'rubygems/defaults' require_relative 'rubygems/deprecate' require_relative 'rubygems/errors' ## # RubyGems is the Ruby standard for publishing and managing third party # libraries. # # For user documentation, see: # # * <tt>gem help</tt> and <tt>gem help [command]</tt> # * {RubyGems User Guide}[https://guides.rubygems.org/] # * {Frequently Asked Questions}[https://guides.rubygems.org/faqs] # # For gem developer documentation see: # # * {Creating Gems}[https://guides.rubygems.org/make-your-own-gem] # * Gem::Specification # * Gem::Version for version dependency notes # # Further RubyGems documentation can be found at: # # * {RubyGems Guides}[https://guides.rubygems.org] # * {RubyGems API}[https://www.rubydoc.info/github/rubygems/rubygems] (also available from # <tt>gem server</tt>) # # == RubyGems Plugins # # RubyGems will load plugins in the latest version of each installed gem or # $LOAD_PATH. Plugins must be named 'rubygems_plugin' (.rb, .so, etc) and # placed at the root of your gem's #require_path. Plugins are installed at a # special location and loaded on boot. # # For an example plugin, see the {Graph gem}[https://github.com/seattlerb/graph] # which adds a `gem graph` command. # # == RubyGems Defaults, Packaging # # RubyGems defaults are stored in lib/rubygems/defaults.rb. If you're packaging # RubyGems or implementing Ruby you can change RubyGems' defaults. # # For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb # and override any defaults from lib/rubygems/defaults.rb. # # For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and # override any defaults from lib/rubygems/defaults.rb. # # If you need RubyGems to perform extra work on install or uninstall, your # defaults override file can set pre/post install and uninstall hooks. # See Gem::pre_install, Gem::pre_uninstall, Gem::post_install, # Gem::post_uninstall. # # == Bugs # # You can submit bugs to the # {RubyGems bug tracker}[https://github.com/rubygems/rubygems/issues] # on GitHub # # == Credits # # RubyGems is currently maintained by Eric Hodel. # # RubyGems was originally developed at RubyConf 2003 by: # # * Rich Kilmer -- rich(at)infoether.com # * Chad Fowler -- chad(at)chadfowler.com # * David Black -- dblack(at)wobblini.net # * Paul Brannan -- paul(at)atdesk.com # * Jim Weirich -- jim(at)weirichhouse.org # # Contributors: # # * Gavin Sinclair -- gsinclair(at)soyabean.com.au # * George Marrows -- george.marrows(at)ntlworld.com # * Dick Davies -- rasputnik(at)hellooperator.net # * Mauricio Fernandez -- batsman.geo(at)yahoo.com # * Simon Strandgaard -- neoneye(at)adslhome.dk # * Dave Glasser -- glasser(at)mit.edu # * Paul Duncan -- pabs(at)pablotron.org # * Ville Aine -- vaine(at)cs.helsinki.fi # * Eric Hodel -- drbrain(at)segment7.net # * Daniel Berger -- djberg96(at)gmail.com # * Phil Hagelberg -- technomancy(at)gmail.com # * Ryan Davis -- ryand-ruby(at)zenspider.com # * Evan Phoenix -- evan(at)fallingsnow.net # * Steve Klabnik -- steve(at)steveklabnik.com # # (If your name is missing, PLEASE let us know!) # # == License # # See {LICENSE.txt}[rdoc-ref:lib/rubygems/LICENSE.txt] for permissions. # # Thanks! # # -The RubyGems Team module Gem RUBYGEMS_DIR = File.dirname File.expand_path(__FILE__) # Taint support is deprecated in Ruby 2.7. # This allows switching ".untaint" to ".tap(&Gem::UNTAINT)", # to avoid deprecation warnings in Ruby 2.7. UNTAINT = RUBY_VERSION < '2.7' ? :untaint.to_sym : proc{} # When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn KERNEL_WARN_IGNORES_INTERNAL_ENTRIES = RUBY_ENGINE == "truffleruby" || (RUBY_ENGINE == "ruby" && RUBY_VERSION >= '3.0') ## # An Array of Regexps that match windows Ruby platforms. WIN_PATTERNS = [ /bccwin/i, /cygwin/i, /djgpp/i, /mingw/i, /mswin/i, /wince/i, ].freeze GEM_DEP_FILES = %w[ gem.deps.rb gems.rb Gemfile Isolate ].freeze ## # Subdirectories in a gem repository REPOSITORY_SUBDIRECTORIES = %w[ build_info cache doc extensions gems plugins specifications ].freeze ## # Subdirectories in a gem repository for default gems REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES = %w[ gems specifications/default ].freeze ## # Exception classes used in a Gem.read_binary +rescue+ statement READ_BINARY_ERRORS = [Errno::EACCES, Errno::EROFS, Errno::ENOSYS, Errno::ENOTSUP].freeze ## # Exception classes used in Gem.write_binary +rescue+ statement WRITE_BINARY_ERRORS = [Errno::ENOSYS, Errno::ENOTSUP].freeze @@win_platform = nil @configuration = nil @gemdeps = nil @loaded_specs = {} LOADED_SPECS_MUTEX = Thread::Mutex.new @path_to_default_spec_map = {} @platforms = [] @ruby = nil @ruby_api_version = nil @sources = nil @post_build_hooks ||= [] @post_install_hooks ||= [] @post_uninstall_hooks ||= [] @pre_uninstall_hooks ||= [] @pre_install_hooks ||= [] @pre_reset_hooks ||= [] @post_reset_hooks ||= [] @default_source_date_epoch = nil ## # Try to activate a gem containing +path+. Returns true if # activation succeeded or wasn't needed because it was already # activated. Returns false if it can't find the path in a gem. def self.try_activate(path) # finds the _latest_ version... regardless of loaded specs and their deps # if another gem had a requirement that would mean we shouldn't # activate the latest version, then either it would already be activated # or if it was ambiguous (and thus unresolved) the code in our custom # require will try to activate the more specific version. spec = Gem::Specification.find_by_path path return false unless spec return true if spec.activated? begin spec.activate rescue Gem::LoadError => e # this could fail due to gem dep collisions, go lax spec_by_name = Gem::Specification.find_by_name(spec.name) if spec_by_name.nil? raise e else spec_by_name.activate end end return true end def self.needs rs = Gem::RequestSet.new yield rs finish_resolve rs end def self.finish_resolve(request_set=Gem::RequestSet.new) request_set.import Gem::Specification.unresolved_deps.values request_set.import Gem.loaded_specs.values.map {|s| Gem::Dependency.new(s.name, s.version) } request_set.resolve_current.each do |s| s.full_spec.activate end end ## # Find the full path to the executable for gem +name+. If the +exec_name+ # is not given, an exception will be raised, otherwise the # specified executable's path is returned. +requirements+ allows # you to specify specific gem versions. def self.bin_path(name, exec_name = nil, *requirements) requirements = Gem::Requirement.default if requirements.empty? find_spec_for_exe(name, exec_name, requirements).bin_file exec_name end def self.find_spec_for_exe(name, exec_name, requirements) raise ArgumentError, "you must supply exec_name" unless exec_name dep = Gem::Dependency.new name, requirements loaded = Gem.loaded_specs[name] return loaded if loaded && dep.matches_spec?(loaded) specs = dep.matching_specs(true) specs = specs.find_all do |spec| spec.executables.include? exec_name end if exec_name unless spec = specs.first msg = "can't find gem #{dep} with executable #{exec_name}" if dep.filters_bundler? && bundler_message = Gem::BundlerVersionFinder.missing_version_message msg = bundler_message end raise Gem::GemNotFoundException, msg end spec end private_class_method :find_spec_for_exe ## # Find the full path to the executable for gem +name+. If the +exec_name+ # is not given, an exception will be raised, otherwise the # specified executable's path is returned. +requirements+ allows # you to specify specific gem versions. # # A side effect of this method is that it will activate the gem that # contains the executable. # # This method should *only* be used in bin stub files. def self.activate_bin_path(name, exec_name = nil, *requirements) # :nodoc: spec = find_spec_for_exe name, exec_name, requirements Gem::LOADED_SPECS_MUTEX.synchronize do spec.activate finish_resolve end spec.bin_file exec_name end ## # The mode needed to read a file as straight binary. def self.binary_mode 'rb' end ## # The path where gem executables are to be installed. def self.bindir(install_dir=Gem.dir) return File.join install_dir, 'bin' unless install_dir.to_s == Gem.default_dir.to_s Gem.default_bindir end ## # The path were rubygems plugins are to be installed. def self.plugindir(install_dir=Gem.dir) File.join install_dir, 'plugins' end ## # Reset the +dir+ and +path+ values. The next time +dir+ or +path+ # is requested, the values will be calculated from scratch. This is # mainly used by the unit tests to provide test isolation. def self.clear_paths @paths = nil @user_home = nil Gem::Specification.reset Gem::Security.reset if defined?(Gem::Security) end ## # The standard configuration object for gems. def self.configuration @configuration ||= Gem::ConfigFile.new [] end ## # Use the given configuration object (which implements the ConfigFile # protocol) as the standard configuration object. def self.configuration=(config) @configuration = config end ## # The path to the data directory specified by the gem name. If the # package is not available as a gem, return nil. def self.datadir(gem_name) spec = @loaded_specs[gem_name] return nil if spec.nil? spec.datadir end ## # A Zlib::Deflate.deflate wrapper def self.deflate(data) require 'zlib' Zlib::Deflate.deflate data end # Retrieve the PathSupport object that RubyGems uses to # lookup files. def self.paths @paths ||= Gem::PathSupport.new(ENV) end # Initialize the filesystem paths to use from +env+. # +env+ is a hash-like object (typically ENV) that # is queried for 'GEM_HOME', 'GEM_PATH', and 'GEM_SPEC_CACHE' # Keys for the +env+ hash should be Strings, and values of the hash should # be Strings or +nil+. def self.paths=(env) clear_paths target = {} env.each_pair do |k,v| case k when 'GEM_HOME', 'GEM_PATH', 'GEM_SPEC_CACHE' case v when nil, String target[k] = v when Array unless Gem::Deprecate.skip warn <<-EOWARN Array values in the parameter to `Gem.paths=` are deprecated. Please use a String or nil. An Array (#{env.inspect}) was passed in from #{caller[3]} EOWARN end target[k] = v.join File::PATH_SEPARATOR end else target[k] = v end end @paths = Gem::PathSupport.new ENV.to_hash.merge(target) Gem::Specification.dirs = @paths.path end ## # The path where gems are to be installed. def self.dir paths.home end def self.path paths.path end def self.spec_cache_dir paths.spec_cache_dir end ## # Quietly ensure the Gem directory +dir+ contains all the proper # subdirectories. If we can't create a directory due to a permission # problem, then we will silently continue. # # If +mode+ is given, missing directories are created with this mode. # # World-writable directories will never be created. def self.ensure_gem_subdirectories(dir = Gem.dir, mode = nil) ensure_subdirectories(dir, mode, REPOSITORY_SUBDIRECTORIES) end ## # Quietly ensure the Gem directory +dir+ contains all the proper # subdirectories for handling default gems. If we can't create a # directory due to a permission problem, then we will silently continue. # # If +mode+ is given, missing directories are created with this mode. # # World-writable directories will never be created. def self.ensure_default_gem_subdirectories(dir = Gem.dir, mode = nil) ensure_subdirectories(dir, mode, REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES) end def self.ensure_subdirectories(dir, mode, subdirs) # :nodoc: old_umask = File.umask File.umask old_umask | 002 require 'fileutils' options = {} options[:mode] = mode if mode subdirs.each do |name| subdir = File.join dir, name next if File.exist? subdir begin FileUtils.mkdir_p subdir, **options rescue SystemCallError end end ensure File.umask old_umask end ## # The extension API version of ruby. This includes the static vs non-static # distinction as extensions cannot be shared between the two. def self.extension_api_version # :nodoc: if 'no' == RbConfig::CONFIG['ENABLE_SHARED'] "#{ruby_api_version}-static" else ruby_api_version end end ## # Returns a list of paths matching +glob+ that can be used by a gem to pick # up features from other gems. For example: # # Gem.find_files('rdoc/discover').each do |path| load path end # # if +check_load_path+ is true (the default), then find_files also searches # $LOAD_PATH for files as well as gems. # # Note that find_files will return all files even if they are from different # versions of the same gem. See also find_latest_files def self.find_files(glob, check_load_path=true) files = [] files = find_files_from_load_path glob if check_load_path gem_specifications = @gemdeps ? Gem.loaded_specs.values : Gem::Specification.stubs files.concat gem_specifications.map {|spec| spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}") }.flatten # $LOAD_PATH might contain duplicate entries or reference # the spec dirs directly, so we prune. files.uniq! if check_load_path return files end def self.find_files_from_load_path(glob) # :nodoc: glob_with_suffixes = "#{glob}#{Gem.suffix_pattern}" $LOAD_PATH.map do |load_path| Gem::Util.glob_files_in_dir(glob_with_suffixes, load_path) end.flatten.select {|file| File.file? file.tap(&Gem::UNTAINT) } end ## # Returns a list of paths matching +glob+ from the latest gems that can be # used by a gem to pick up features from other gems. For example: # # Gem.find_latest_files('rdoc/discover').each do |path| load path end # # if +check_load_path+ is true (the default), then find_latest_files also # searches $LOAD_PATH for files as well as gems. # # Unlike find_files, find_latest_files will return only files from the # latest version of a gem. def self.find_latest_files(glob, check_load_path=true) files = [] files = find_files_from_load_path glob if check_load_path files.concat Gem::Specification.latest_specs(true).map {|spec| spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}") }.flatten # $LOAD_PATH might contain duplicate entries or reference # the spec dirs directly, so we prune. files.uniq! if check_load_path return files end ## # Top level install helper method. Allows you to install gems interactively: # # % irb # >> Gem.install "minitest" # Fetching: minitest-5.14.0.gem (100%) # => [#<Gem::Specification:0x1013b4528 @name="minitest", ...>] def self.install(name, version = Gem::Requirement.default, *options) require_relative "rubygems/dependency_installer" inst = Gem::DependencyInstaller.new(*options) inst.install name, version inst.installed_gems end ## # Get the default RubyGems API host. This is normally # <tt>https://rubygems.org</tt>. def self.host @host ||= Gem::DEFAULT_HOST end ## Set the default RubyGems API host. def self.host=(host) @host = host end ## # The index to insert activated gem paths into the $LOAD_PATH. The activated # gem's paths are inserted before site lib directory by default. def self.load_path_insert_index $LOAD_PATH.each_with_index do |path, i| return i if path.instance_variable_defined?(:@gem_prelude_index) end index = $LOAD_PATH.index RbConfig::CONFIG['sitelibdir'] index || 0 end ## # The number of paths in the `$LOAD_PATH` from activated gems. Used to # prioritize `-I` and `ENV['RUBYLIB`]` entries during `require`. def self.activated_gem_paths @activated_gem_paths ||= 0 end ## # Add a list of paths to the $LOAD_PATH at the proper place. def self.add_to_load_path(*paths) @activated_gem_paths = activated_gem_paths + paths.size # gem directories must come after -I and ENV['RUBYLIB'] $LOAD_PATH.insert(Gem.load_path_insert_index, *paths) end @yaml_loaded = false ## # Loads YAML, preferring Psych def self.load_yaml return if @yaml_loaded begin # Try requiring the gem version *or* stdlib version of psych. require 'psych' rescue ::LoadError # If we can't load psych, that's fine, go on. else require_relative 'rubygems/psych_additions' require_relative 'rubygems/psych_tree' end require 'yaml' require_relative 'rubygems/safe_yaml' @yaml_loaded = true end ## # The file name and line number of the caller of the caller of this method. # # +depth+ is how many layers up the call stack it should go. # # e.g., # # def a; Gem.location_of_caller; end # a #=> ["x.rb", 2] # (it'll vary depending on file name and line number) # # def b; c; end # def c; Gem.location_of_caller(2); end # b #=> ["x.rb", 6] # (it'll vary depending on file name and line number) def self.location_of_caller(depth = 1) caller[depth] =~ /(.*?):(\d+).*?$/i file = $1 lineno = $2.to_i [file, lineno] end ## # The version of the Marshal format for your Ruby. def self.marshal_version "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" end ## # Set array of platforms this RubyGems supports (primarily for testing). def self.platforms=(platforms) @platforms = platforms end ## # Array of platforms this RubyGems supports. def self.platforms @platforms ||= [] if @platforms.empty? @platforms = [Gem::Platform::RUBY, Gem::Platform.local] end @platforms end ## # Adds a post-build hook that will be passed an Gem::Installer instance # when Gem::Installer#install is called. The hook is called after the gem # has been extracted and extensions have been built but before the # executables or gemspec has been written. If the hook returns +false+ then # the gem's files will be removed and the install will be aborted. def self.post_build(&hook) @post_build_hooks << hook end ## # Adds a post-install hook that will be passed an Gem::Installer instance # when Gem::Installer#install is called def self.post_install(&hook) @post_install_hooks << hook end ## # Adds a post-installs hook that will be passed a Gem::DependencyInstaller # and a list of installed specifications when # Gem::DependencyInstaller#install is complete def self.done_installing(&hook) @done_installing_hooks << hook end ## # Adds a hook that will get run after Gem::Specification.reset is # run. def self.post_reset(&hook) @post_reset_hooks << hook end ## # Adds a post-uninstall hook that will be passed a Gem::Uninstaller instance # and the spec that was uninstalled when Gem::Uninstaller#uninstall is # called def self.post_uninstall(&hook) @post_uninstall_hooks << hook end ## # Adds a pre-install hook that will be passed an Gem::Installer instance # when Gem::Installer#install is called. If the hook returns +false+ then # the install will be aborted. def self.pre_install(&hook) @pre_install_hooks << hook end ## # Adds a hook that will get run before Gem::Specification.reset is # run. def self.pre_reset(&hook) @pre_reset_hooks << hook end ## # Adds a pre-uninstall hook that will be passed an Gem::Uninstaller instance # and the spec that will be uninstalled when Gem::Uninstaller#uninstall is # called def self.pre_uninstall(&hook) @pre_uninstall_hooks << hook end ## # The directory prefix this RubyGems was installed at. If your # prefix is in a standard location (ie, rubygems is installed where # you'd expect it to be), then prefix returns nil. def self.prefix prefix = File.dirname RUBYGEMS_DIR if prefix != File.expand_path(RbConfig::CONFIG['sitelibdir']) and prefix != File.expand_path(RbConfig::CONFIG['libdir']) and 'lib' == File.basename(RUBYGEMS_DIR) prefix end end ## # Refresh available gems from disk. def self.refresh Gem::Specification.reset end ## # Safely read a file in binary mode on all platforms. def self.read_binary(path) File.open path, 'rb+' do |f| f.flock(File::LOCK_EX) f.read end rescue *READ_BINARY_ERRORS File.open path, 'rb' do |f| f.read end rescue Errno::ENOLCK # NFS if Thread.main != Thread.current raise else File.open path, 'rb' do |f| f.read end end end ## # Safely write a file in binary mode on all platforms. def self.write_binary(path, data) File.open(path, File::RDWR | File::CREAT | File::BINARY | File::LOCK_EX) do |io| io.write data end rescue *WRITE_BINARY_ERRORS File.open(path, 'wb') do |io| io.write data end rescue Errno::ENOLCK # NFS if Thread.main != Thread.current raise else File.open(path, 'wb') do |io| io.write data end end end ## # The path to the running Ruby interpreter. def self.ruby if @ruby.nil? @ruby = RbConfig.ruby @ruby = "\"#{@ruby}\"" if @ruby =~ /\s/ end @ruby end ## # Returns a String containing the API compatibility version of Ruby def self.ruby_api_version @ruby_api_version ||= RbConfig::CONFIG['ruby_version'].dup end def self.env_requirement(gem_name) @env_requirements_by_name ||= {} @env_requirements_by_name[gem_name] ||= begin req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || '>= 0'.freeze Gem::Requirement.create(req) end end post_reset { @env_requirements_by_name = {} } ## # Returns the latest release-version specification for the gem +name+. def self.latest_spec_for(name) dependency = Gem::Dependency.new name fetcher = Gem::SpecFetcher.fetcher spec_tuples, = fetcher.spec_for_dependency dependency spec, = spec_tuples.first spec end ## # Returns the latest release version of RubyGems. def self.latest_rubygems_version latest_version_for('rubygems-update') or raise "Can't find 'rubygems-update' in any repo. Check `gem source list`." end ## # Returns the version of the latest release-version of gem +name+ def self.latest_version_for(name) spec = latest_spec_for name spec and spec.version end ## # A Gem::Version for the currently running Ruby. def self.ruby_version return @ruby_version if defined? @ruby_version version = RUBY_VERSION.dup if defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1 version << ".#{RUBY_PATCHLEVEL}" elsif defined?(RUBY_DESCRIPTION) if RUBY_ENGINE == "ruby" desc = RUBY_DESCRIPTION[/\Aruby #{Regexp.quote(RUBY_VERSION)}([^ ]+) /, 1] else desc = RUBY_DESCRIPTION[/\A#{RUBY_ENGINE} #{Regexp.quote(RUBY_ENGINE_VERSION)} \(#{RUBY_VERSION}([^ ]+)\) /, 1] end version << ".#{desc}" if desc end @ruby_version = Gem::Version.new version end ## # A Gem::Version for the currently running RubyGems def self.rubygems_version return @rubygems_version if defined? @rubygems_version @rubygems_version = Gem::Version.new Gem::VERSION end ## # Returns an Array of sources to fetch remote gems from. Uses # default_sources if the sources list is empty. def self.sources source_list = configuration.sources || default_sources @sources ||= Gem::SourceList.from(source_list) end ## # Need to be able to set the sources without calling # Gem.sources.replace since that would cause an infinite loop. # # DOC: This comment is not documentation about the method itself, it's # more of a code comment about the implementation. def self.sources=(new_sources) if !new_sources @sources = nil else @sources = Gem::SourceList.from(new_sources) end end ## # Glob pattern for require-able path suffixes. def self.suffix_pattern @suffix_pattern ||= "{#{suffixes.join(',')}}" end ## # Regexp for require-able path suffixes. def self.suffix_regexp @suffix_regexp ||= /#{Regexp.union(suffixes)}\z/ end ## # Glob pattern for require-able plugin suffixes. def self.plugin_suffix_pattern @plugin_suffix_pattern ||= "_plugin#{suffix_pattern}" end ## # Regexp for require-able plugin suffixes. def self.plugin_suffix_regexp @plugin_suffix_regexp ||= /_plugin#{suffix_regexp}\z/ end ## # Suffixes for require-able paths. def self.suffixes @suffixes ||= ['', '.rb', *%w[DLEXT DLEXT2].map do |key| val = RbConfig::CONFIG[key] next unless val and not val.empty? ".#{val}" end, ].compact.uniq end ## # Prints the amount of time the supplied block takes to run using the debug # UI output. def self.time(msg, width = 0, display = Gem.configuration.verbose) now = Time.now value = yield elapsed = Time.now - now ui.say "%2$*1$s: %3$3.3fs" % [-width, msg, elapsed] if display value end ## # Lazily loads DefaultUserInteraction and returns the default UI. def self.ui require_relative 'rubygems/user_interaction' Gem::DefaultUserInteraction.ui end ## # Use the +home+ and +paths+ values for Gem.dir and Gem.path. Used mainly # by the unit tests to provide environment isolation. def self.use_paths(home, *paths) paths.flatten! paths.compact! hash = { "GEM_HOME" => home, "GEM_PATH" => paths.empty? ? home : paths.join(File::PATH_SEPARATOR) } hash.delete_if {|_, v| v.nil? } self.paths = hash end ## # Is this a windows platform? def self.win_platform? if @@win_platform.nil? ruby_platform = RbConfig::CONFIG['host_os'] @@win_platform = !!WIN_PATTERNS.find {|r| ruby_platform =~ r } end @@win_platform end ## # Is this a java platform? def self.java_platform? RUBY_PLATFORM == "java" end ## # Load +plugins+ as Ruby files def self.load_plugin_files(plugins) # :nodoc: plugins.each do |plugin| # Skip older versions of the GemCutter plugin: Its commands are in # RubyGems proper now. next if plugin =~ /gemcutter-0\.[0-3]/ begin load plugin rescue ::Exception => e details = "#{plugin.inspect}: #{e.message} (#{e.class})" warn "Error loading RubyGems plugin #{details}" end end end ## # Find rubygems plugin files in the standard location and load them def self.load_plugins Gem.path.each do |gem_path| load_plugin_files Gem::Util.glob_files_in_dir("*#{Gem.plugin_suffix_pattern}", plugindir(gem_path)) end end ## # Find all 'rubygems_plugin' files in $LOAD_PATH and load them def self.load_env_plugins load_plugin_files find_files_from_load_path("rubygems_plugin") end ## # Looks for a gem dependency file at +path+ and activates the gems in the # file if found. If the file is not found an ArgumentError is raised. # # If +path+ is not given the RUBYGEMS_GEMDEPS environment variable is used, # but if no file is found no exception is raised. # # If '-' is given for +path+ RubyGems searches up from the current working # directory for gem dependency files (gem.deps.rb, Gemfile, Isolate) and # activates the gems in the first one found. # # You can run this automatically when rubygems starts. To enable, set # the <code>RUBYGEMS_GEMDEPS</code> environment variable to either the path # of your gem dependencies file or "-" to auto-discover in parent # directories. # # NOTE: Enabling automatic discovery on multiuser systems can lead to # execution of arbitrary code when used from directories outside your # control. def self.use_gemdeps(path = nil) raise_exception = path path ||= ENV['RUBYGEMS_GEMDEPS'] return unless path path = path.dup if path == "-" Gem::Util.traverse_parents Dir.pwd do |directory| dep_file = GEM_DEP_FILES.find {|f| File.file?(f) } next unless dep_file path = File.join directory, dep_file break end end path.tap(&Gem::UNTAINT) unless File.file? path return unless raise_exception raise ArgumentError, "Unable to find gem dependencies file at #{path}" end ENV["BUNDLE_GEMFILE"] ||= File.expand_path(path) require_relative 'rubygems/user_interaction' require "bundler" begin Gem::DefaultUserInteraction.use_ui(ui) do begin Bundler.ui.silence do @gemdeps = Bundler.setup end ensure Gem::DefaultUserInteraction.ui.close end end rescue Bundler::BundlerError => e warn e.message warn "You may need to `bundle install` to install missing gems" warn "" end end ## # If the SOURCE_DATE_EPOCH environment variable is set, returns it's value. # Otherwise, returns the time that `Gem.source_date_epoch_string` was # first called in the same format as SOURCE_DATE_EPOCH. # # NOTE(@duckinator): The implementation is a tad weird because we want to: # 1. Make builds reproducible by default, by having this function always # return the same result during a given run. # 2. Allow changing ENV['SOURCE_DATE_EPOCH'] at runtime, since multiple # tests that set this variable will be run in a single process. # # If you simplify this function and a lot of tests fail, that is likely # due to #2 above. # # Details on SOURCE_DATE_EPOCH: # https://reproducible-builds.org/specs/source-date-epoch/ def self.source_date_epoch_string # The value used if $SOURCE_DATE_EPOCH is not set. @default_source_date_epoch ||= Time.now.to_i.to_s specified_epoch = ENV["SOURCE_DATE_EPOCH"] # If it's empty or just whitespace, treat it like it wasn't set at all. specified_epoch = nil if !specified_epoch.nil? && specified_epoch.strip.empty? epoch = specified_epoch || @default_source_date_epoch epoch.strip end ## # Returns the value of Gem.source_date_epoch_string, as a Time object. # # This is used throughout RubyGems for enabling reproducible builds. def self.source_date_epoch Time.at(self.source_date_epoch_string.to_i).utc.freeze end # FIX: Almost everywhere else we use the `def self.` way of defining class # methods, and then we switch over to `class << self` here. Pick one or the # other. class << self ## # RubyGems distributors (like operating system package managers) can # disable RubyGems update by setting this to error message printed to # end-users on gem update --system instead of actual update. attr_accessor :disable_system_update_message ## # Hash of loaded Gem::Specification keyed by name attr_reader :loaded_specs ## # GemDependencyAPI object, which is set when .use_gemdeps is called. # This contains all the information from the Gemfile. attr_reader :gemdeps ## # Register a Gem::Specification for default gem. # # Two formats for the specification are supported: # # * MRI 2.0 style, where spec.files contains unprefixed require names. # The spec's filenames will be registered as-is. # * New style, where spec.files contains files prefixed with paths # from spec.require_paths. The prefixes are stripped before # registering the spec's filenames. Unprefixed files are omitted. # def register_default_spec(spec) extended_require_paths = spec.require_paths.map {|f| f + "/" } new_format = extended_require_paths.any? {|path| spec.files.any? {|f| f.start_with? path } } if new_format prefix_group = extended_require_paths.join("|") prefix_pattern = /^(#{prefix_group})/ end spec.files.each do |file| if new_format file = file.sub(prefix_pattern, "") next unless $~ end spec.activate if already_loaded?(file) @path_to_default_spec_map[file] = spec @path_to_default_spec_map[file.sub(suffix_regexp, "")] = spec end end ## # Find a Gem::Specification of default gem from +path+ def find_unresolved_default_spec(path) default_spec = @path_to_default_spec_map[path] return default_spec if default_spec && loaded_specs[default_spec.name] != default_spec end ## # Clear default gem related variables. It is for test def clear_default_specs @path_to_default_spec_map.clear end ## # The list of hooks to be run after Gem::Installer#install extracts files # and builds extensions attr_reader :post_build_hooks ## # The list of hooks to be run after Gem::Installer#install completes # installation attr_reader :post_install_hooks ## # The list of hooks to be run after Gem::DependencyInstaller installs a # set of gems attr_reader :done_installing_hooks ## # The list of hooks to be run after Gem::Specification.reset is run. attr_reader :post_reset_hooks ## # The list of hooks to be run after Gem::Uninstaller#uninstall completes # installation attr_reader :post_uninstall_hooks ## # The list of hooks to be run before Gem::Installer#install does any work attr_reader :pre_install_hooks ## # The list of hooks to be run before Gem::Specification.reset is run. attr_reader :pre_reset_hooks ## # The list of hooks to be run before Gem::Uninstaller#uninstall does any # work attr_reader :pre_uninstall_hooks private def already_loaded?(file) $LOADED_FEATURES.any? do |feature_path| feature_path.end_with?(file) && default_gem_load_paths.any? {|load_path_entry| feature_path == "#{load_path_entry}/#{file}" } end end def default_gem_load_paths @default_gem_load_paths ||= $LOAD_PATH[load_path_insert_index..-1].map do |lp| expanded = File.expand_path(lp) next expanded unless File.exist?(expanded) File.realpath(expanded) end end end ## # Location of Marshal quick gemspecs on remote repositories MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze autoload :BundlerVersionFinder, File.expand_path('rubygems/bundler_version_finder', __dir__) autoload :ConfigFile, File.expand_path('rubygems/config_file', __dir__) autoload :Dependency, File.expand_path('rubygems/dependency', __dir__) autoload :DependencyList, File.expand_path('rubygems/dependency_list', __dir__) autoload :Installer, File.expand_path('rubygems/installer', __dir__) autoload :Licenses, File.expand_path('rubygems/util/licenses', __dir__) autoload :NameTuple, File.expand_path('rubygems/name_tuple', __dir__) autoload :PathSupport, File.expand_path('rubygems/path_support', __dir__) autoload :RequestSet, File.expand_path('rubygems/request_set', __dir__) autoload :Resolver, File.expand_path('rubygems/resolver', __dir__) autoload :Source, File.expand_path('rubygems/source', __dir__) autoload :SourceList, File.expand_path('rubygems/source_list', __dir__) autoload :SpecFetcher, File.expand_path('rubygems/spec_fetcher', __dir__) autoload :Util, File.expand_path('rubygems/util', __dir__) autoload :Version, File.expand_path('rubygems/version', __dir__) end require_relative 'rubygems/exceptions' require_relative 'rubygems/specification' # REFACTOR: This should be pulled out into some kind of hacks file. begin ## # Defaults the operating system (or packager) wants to provide for RubyGems. require 'rubygems/defaults/operating_system' rescue LoadError # Ignored rescue StandardError => e msg = "#{e.message}\n" \ "Loading the rubygems/defaults/operating_system.rb file caused an error. " \ "This file is owned by your OS, not by rubygems upstream. " \ "Please find out which OS package this file belongs to and follow the guidelines from your OS to report " \ "the problem and ask for help." raise e.class, msg end begin ## # Defaults the Ruby implementation wants to provide for RubyGems require "rubygems/defaults/#{RUBY_ENGINE}" rescue LoadError end ## # Loads the default specs. Gem::Specification.load_defaults require_relative 'rubygems/core_ext/kernel_gem' require_relative 'rubygems/core_ext/kernel_require' require_relative 'rubygems/core_ext/kernel_warn' rubygems/rubygems/command.rb 0000644 00000037645 15102661023 0012214 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'optparse' require_relative 'requirement' require_relative 'user_interaction' ## # Base class for all Gem commands. When creating a new gem command, define # #initialize, #execute, #arguments, #defaults_str, #description and #usage # (as appropriate). See the above mentioned methods for details. # # A very good example to look at is Gem::Commands::ContentsCommand class Gem::Command include Gem::UserInteraction Gem::OptionParser.accept Symbol do |value| value.to_sym end ## # The name of the command. attr_reader :command ## # The options for the command. attr_reader :options ## # The default options for the command. attr_accessor :defaults ## # The name of the command for command-line invocation. attr_accessor :program_name ## # A short description of the command. attr_accessor :summary ## # Arguments used when building gems def self.build_args @build_args ||= [] end def self.build_args=(value) @build_args = value end def self.common_options @common_options ||= [] end def self.add_common_option(*args, &handler) Gem::Command.common_options << [args, handler] end def self.extra_args @extra_args ||= [] end def self.extra_args=(value) case value when Array @extra_args = value when String @extra_args = value.split(' ') end end ## # Return an array of extra arguments for the command. The extra arguments # come from the gem configuration file read at program startup. def self.specific_extra_args(cmd) specific_extra_args_hash[cmd] end ## # Add a list of extra arguments for the given command. +args+ may be an # array or a string to be split on white space. def self.add_specific_extra_args(cmd,args) args = args.split(/\s+/) if args.kind_of? String specific_extra_args_hash[cmd] = args end ## # Accessor for the specific extra args hash (self initializing). def self.specific_extra_args_hash @specific_extra_args_hash ||= Hash.new do |h,k| h[k] = Array.new end end ## # Initializes a generic gem command named +command+. +summary+ is a short # description displayed in `gem help commands`. +defaults+ are the default # options. Defaults should be mirrored in #defaults_str, unless there are # none. # # When defining a new command subclass, use add_option to add command-line # switches. # # Unhandled arguments (gem names, files, etc.) are left in # <tt>options[:args]</tt>. def initialize(command, summary=nil, defaults={}) @command = command @summary = summary @program_name = "gem #{command}" @defaults = defaults @options = defaults.dup @option_groups = Hash.new {|h,k| h[k] = [] } @deprecated_options = { command => {} } @parser = nil @when_invoked = nil end ## # True if +long+ begins with the characters from +short+. def begins?(long, short) return false if short.nil? long[0, short.length] == short end ## # Override to provide command handling. # # #options will be filled in with your parsed options, unparsed options will # be left in <tt>options[:args]</tt>. # # See also: #get_all_gem_names, #get_one_gem_name, # #get_one_optional_argument def execute raise Gem::Exception, "generic command has no actions" end ## # Display to the user that a gem couldn't be found and reasons why #-- def show_lookup_failure(gem_name, version, errors, suppress_suggestions = false, required_by = nil) gem = "'#{gem_name}' (#{version})" msg = String.new "Could not find a valid gem #{gem}" if errors and !errors.empty? msg << ", here is why:\n" errors.each {|x| msg << " #{x.wordy}\n" } else if required_by and gem != required_by msg << " (required by #{required_by}) in any repository" else msg << " in any repository" end end alert_error msg unless suppress_suggestions suggestions = Gem::SpecFetcher.fetcher.suggest_gems_from_name(gem_name, :latest, 10) unless suggestions.empty? alert_error "Possible alternatives: #{suggestions.join(", ")}" end end end ## # Get all gem names from the command line. def get_all_gem_names args = options[:args] if args.nil? or args.empty? raise Gem::CommandLineError, "Please specify at least one gem name (e.g. gem build GEMNAME)" end args.select {|arg| arg !~ /^-/ } end ## # Get all [gem, version] from the command line. # # An argument in the form gem:ver is pull apart into the gen name and version, # respectively. def get_all_gem_names_and_versions get_all_gem_names.map do |name| if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name [$1, $2] else [name] end end end ## # Get a single gem name from the command line. Fail if there is no gem name # or if there is more than one gem name given. def get_one_gem_name args = options[:args] if args.nil? or args.empty? raise Gem::CommandLineError, "Please specify a gem name on the command line (e.g. gem build GEMNAME)" end if args.size > 1 raise Gem::CommandLineError, "Too many gem names (#{args.join(', ')}); please specify only one" end args.first end ## # Get a single optional argument from the command line. If more than one # argument is given, return only the first. Return nil if none are given. def get_one_optional_argument args = options[:args] || [] args.first end ## # Override to provide details of the arguments a command takes. It should # return a left-justified string, one argument per line. # # For example: # # def usage # "#{program_name} FILE [FILE ...]" # end # # def arguments # "FILE name of file to find" # end def arguments "" end ## # Override to display the default values of the command options. (similar to # +arguments+, but displays the default values). # # For example: # # def defaults_str # --no-gems-first --no-all # end def defaults_str "" end ## # Override to display a longer description of what this command does. def description nil end ## # Override to display the usage for an individual gem command. # # The text "[options]" is automatically appended to the usage text. def usage program_name end ## # Display the help message for the command. def show_help parser.program_name = usage say parser end ## # Invoke the command with the given list of arguments. def invoke(*args) invoke_with_build_args args, nil end ## # Invoke the command with the given list of normal arguments # and additional build arguments. def invoke_with_build_args(args, build_args) handle_options args options[:build_args] = build_args if options[:silent] old_ui = self.ui self.ui = ui = Gem::SilentUI.new end if options[:help] show_help elsif @when_invoked @when_invoked.call options else execute end ensure if ui self.ui = old_ui ui.close end end ## # Call the given block when invoked. # # Normal command invocations just executes the +execute+ method of the # command. Specifying an invocation block allows the test methods to # override the normal action of a command to determine that it has been # invoked correctly. def when_invoked(&block) @when_invoked = block end ## # Add a command-line option and handler to the command. # # See Gem::OptionParser#make_switch for an explanation of +opts+. # # +handler+ will be called with two values, the value of the argument and # the options hash. # # If the first argument of add_option is a Symbol, it's used to group # options in output. See `gem help list` for an example. def add_option(*opts, &handler) # :yields: value, options group_name = Symbol === opts.first ? opts.shift : :options raise "Do not pass an empty string in opts" if opts.include?("") @option_groups[group_name] << [opts, handler] end ## # Remove previously defined command-line argument +name+. def remove_option(name) @option_groups.each do |_, option_list| option_list.reject! {|args, _| args.any? {|x| x.is_a?(String) && x =~ /^#{name}/ } } end end ## # Mark a command-line option as deprecated, and optionally specify a # deprecation horizon. # # Note that with the current implementation, every version of the option needs # to be explicitly deprecated, so to deprecate an option defined as # # add_option('-t', '--[no-]test', 'Set test mode') do |value, options| # # ... stuff ... # end # # you would need to explicitly add a call to `deprecate_option` for every # version of the option you want to deprecate, like # # deprecate_option('-t') # deprecate_option('--test') # deprecate_option('--no-test') def deprecate_option(name, version: nil, extra_msg: nil) @deprecated_options[command].merge!({ name => { "rg_version_to_expire" => version, "extra_msg" => extra_msg } }) end def check_deprecated_options(options) options.each do |option| if option_is_deprecated?(option) deprecation = @deprecated_options[command][option] version_to_expire = deprecation["rg_version_to_expire"] deprecate_option_msg = if version_to_expire "The \"#{option}\" option has been deprecated and will be removed in Rubygems #{version_to_expire}." else "The \"#{option}\" option has been deprecated and will be removed in future versions of Rubygems." end extra_msg = deprecation["extra_msg"] deprecate_option_msg += " #{extra_msg}" if extra_msg alert_warning(deprecate_option_msg) end end end ## # Merge a set of command options with the set of default options (without # modifying the default option hash). def merge_options(new_options) @options = @defaults.clone new_options.each {|k,v| @options[k] = v } end ## # True if the command handles the given argument list. def handles?(args) begin parser.parse!(args.dup) return true rescue return false end end ## # Handle the given list of arguments by parsing them and recording the # results. def handle_options(args) args = add_extra_args(args) check_deprecated_options(args) @options = Marshal.load Marshal.dump @defaults # deep copy parser.parse!(args) @options[:args] = args end ## # Adds extra args from ~/.gemrc def add_extra_args(args) result = [] s_extra = Gem::Command.specific_extra_args(@command) extra = Gem::Command.extra_args + s_extra until extra.empty? do ex = [] ex << extra.shift ex << extra.shift if extra.first.to_s =~ /^[^-]/ # rubocop:disable Performance/StartWith result << ex if handles?(ex) end result.flatten! result.concat(args) result end def deprecated? false end private def option_is_deprecated?(option) @deprecated_options[command].has_key?(option) end def add_parser_description # :nodoc: return unless description formatted = description.split("\n\n").map do |chunk| wrap chunk, 80 - 4 end.join "\n" @parser.separator nil @parser.separator " Description:" formatted.split("\n").each do |line| @parser.separator " #{line.rstrip}" end end def add_parser_options # :nodoc: @parser.separator nil regular_options = @option_groups.delete :options configure_options "", regular_options @option_groups.sort_by {|n,_| n.to_s }.each do |group_name, option_list| @parser.separator nil configure_options group_name, option_list end end ## # Adds a section with +title+ and +content+ to the parser help view. Used # for adding command arguments and default arguments. def add_parser_run_info(title, content) return if content.empty? @parser.separator nil @parser.separator " #{title}:" content.split(/\n/).each do |line| @parser.separator " #{line}" end end def add_parser_summary # :nodoc: return unless @summary @parser.separator nil @parser.separator " Summary:" wrap(@summary, 80 - 4).split("\n").each do |line| @parser.separator " #{line.strip}" end end ## # Create on demand parser. def parser create_option_parser if @parser.nil? @parser end ## # Creates an option parser and fills it in with the help info for the # command. def create_option_parser @parser = Gem::OptionParser.new add_parser_options @parser.separator nil configure_options "Common", Gem::Command.common_options add_parser_run_info "Arguments", arguments add_parser_summary add_parser_description add_parser_run_info "Defaults", defaults_str end def configure_options(header, option_list) return if option_list.nil? or option_list.empty? header = header.to_s.empty? ? '' : "#{header} " @parser.separator " #{header}Options:" option_list.each do |args, handler| @parser.on(*args) do |value| handler.call(value, @options) end end @parser.separator '' end ## # Wraps +text+ to +width+ def wrap(text, width) # :doc: text.gsub(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n") end # ---------------------------------------------------------------- # Add the options common to all commands. add_common_option('-h', '--help', 'Get help on this command') do |value, options| options[:help] = true end add_common_option('-V', '--[no-]verbose', 'Set the verbose level of output') do |value, options| # Set us to "really verbose" so the progress meter works if Gem.configuration.verbose and value Gem.configuration.verbose = 1 else Gem.configuration.verbose = value end end add_common_option('-q', '--quiet', 'Silence command progress meter') do |value, options| Gem.configuration.verbose = false end add_common_option("--silent", "Silence RubyGems output") do |value, options| options[:silent] = true end # Backtrace and config-file are added so they show up in the help # commands. Both options are actually handled before the other # options get parsed. add_common_option('--config-file FILE', 'Use this config file instead of default') do end add_common_option('--backtrace', 'Show stack backtrace on errors') do end add_common_option('--debug', 'Turn on Ruby debugging') do end add_common_option('--norc', 'Avoid loading any .gemrc file') do end # :stopdoc: HELP = <<-HELP.freeze RubyGems is a package manager for Ruby. Usage: gem -h/--help gem -v/--version gem command [arguments...] [options...] Examples: gem install rake gem list --local gem build package.gemspec gem push package-0.0.1.gem gem help install Further help: gem help commands list all 'gem' commands gem help examples show some examples of usage gem help gem_dependencies gem dependencies file guide gem help platforms gem platforms guide gem help <COMMAND> show help on COMMAND (e.g. 'gem help install') gem server present a web page at http://localhost:8808/ with info about installed gems Further information: https://guides.rubygems.org HELP # :startdoc: end ## # \Commands will be placed in this namespace module Gem::Commands end rubygems/rubygems/errors.rb 0000644 00000011232 15102661023 0012072 0 ustar 00 # frozen_string_literal: true #-- # This file contains all the various exceptions and other errors that are used # inside of RubyGems. # # DOC: Confirm _all_ #++ module Gem ## # Raised when RubyGems is unable to load or activate a gem. Contains the # name and version requirements of the gem that either conflicts with # already activated gems or that RubyGems is otherwise unable to activate. class LoadError < ::LoadError # Name of gem attr_accessor :name # Version requirement of gem attr_accessor :requirement end ## # Raised when trying to activate a gem, and that gem does not exist on the # system. Instead of rescuing from this class, make sure to rescue from the # superclass Gem::LoadError to catch all types of load errors. class MissingSpecError < Gem::LoadError def initialize(name, requirement, extra_message=nil) @name = name @requirement = requirement @extra_message = extra_message end def message # :nodoc: build_message + "Checked in 'GEM_PATH=#{Gem.path.join(File::PATH_SEPARATOR)}' #{@extra_message}, execute `gem env` for more information" end private def build_message total = Gem::Specification.stubs.size "Could not find '#{name}' (#{requirement}) among #{total} total gem(s)\n" end end ## # Raised when trying to activate a gem, and the gem exists on the system, but # not the requested version. Instead of rescuing from this class, make sure to # rescue from the superclass Gem::LoadError to catch all types of load errors. class MissingSpecVersionError < MissingSpecError attr_reader :specs def initialize(name, requirement, specs) super(name, requirement) @specs = specs end private def build_message if name == "bundler" && message = Gem::BundlerVersionFinder.missing_version_message return message end names = specs.map(&:full_name) "Could not find '#{name}' (#{requirement}) - did find: [#{names.join ','}]\n" end end # Raised when there are conflicting gem specs loaded class ConflictError < LoadError ## # A Hash mapping conflicting specifications to the dependencies that # caused the conflict attr_reader :conflicts ## # The specification that had the conflict attr_reader :target def initialize(target, conflicts) @target = target @conflicts = conflicts @name = target.name reason = conflicts.map do |act, dependencies| "#{act.full_name} conflicts with #{dependencies.join(", ")}" end.join ", " # TODO: improve message by saying who activated `con` super("Unable to activate #{target.full_name}, because #{reason}") end end class ErrorReason; end # Generated when trying to lookup a gem to indicate that the gem # was found, but that it isn't usable on the current platform. # # fetch and install read these and report them to the user to aid # in figuring out why a gem couldn't be installed. # class PlatformMismatch < ErrorReason ## # the name of the gem attr_reader :name ## # the version attr_reader :version ## # The platforms that are mismatched attr_reader :platforms def initialize(name, version) @name = name @version = version @platforms = [] end ## # append a platform to the list of mismatched platforms. # # Platforms are added via this instead of injected via the constructor # so that we can loop over a list of mismatches and just add them rather # than perform some kind of calculation mismatch summary before creation. def add_platform(platform) @platforms << platform end ## # A wordy description of the error. def wordy "Found %s (%s), but was for platform%s %s" % [@name, @version, @platforms.size == 1 ? '' : 's', @platforms.join(' ,')] end end ## # An error that indicates we weren't able to fetch some # data from a source class SourceFetchProblem < ErrorReason ## # Creates a new SourceFetchProblem for the given +source+ and +error+. def initialize(source, error) @source = source @error = error end ## # The source that had the fetch problem. attr_reader :source ## # The fetch error which is an Exception subclass. attr_reader :error ## # An English description of the error. def wordy "Unable to download data from #{Gem::Uri.new(@source.uri).redacted} - #{@error.message}" end ## # The "exception" alias allows you to call raise on a SourceFetchProblem. alias exception error end end rubygems/rubygems/specification.rb 0000644 00000214114 15102661023 0013402 0 ustar 00 # frozen_string_literal: true # -*- coding: utf-8 -*- #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'deprecate' require_relative 'basic_specification' require_relative 'stub_specification' require_relative 'platform' require_relative 'requirement' require_relative 'specification_policy' require_relative 'util/list' ## # The Specification class contains the information for a gem. Typically # defined in a .gemspec file or a Rakefile, and looks like this: # # Gem::Specification.new do |s| # s.name = 'example' # s.version = '0.1.0' # s.licenses = ['MIT'] # s.summary = "This is an example!" # s.description = "Much longer explanation of the example!" # s.authors = ["Ruby Coder"] # s.email = 'rubycoder@example.com' # s.files = ["lib/example.rb"] # s.homepage = 'https://rubygems.org/gems/example' # s.metadata = { "source_code_uri" => "https://github.com/example/example" } # end # # Starting in RubyGems 2.0, a Specification can hold arbitrary # metadata. See #metadata for restrictions on the format and size of metadata # items you may add to a specification. class Gem::Specification < Gem::BasicSpecification extend Gem::Deprecate # REFACTOR: Consider breaking out this version stuff into a separate # module. There's enough special stuff around it that it may justify # a separate class. ## # The version number of a specification that does not specify one # (i.e. RubyGems 0.7 or earlier). NONEXISTENT_SPECIFICATION_VERSION = -1 ## # The specification version applied to any new Specification instances # created. This should be bumped whenever something in the spec format # changes. # # Specification Version History: # # spec ruby # ver ver yyyy-mm-dd description # -1 <0.8.0 pre-spec-version-history # 1 0.8.0 2004-08-01 Deprecated "test_suite_file" for "test_files" # "test_file=x" is a shortcut for "test_files=[x]" # 2 0.9.5 2007-10-01 Added "required_rubygems_version" # Now forward-compatible with future versions # 3 1.3.2 2009-01-03 Added Fixnum validation to specification_version # 4 1.9.0 2011-06-07 Added metadata #-- # When updating this number, be sure to also update #to_ruby. # # NOTE RubyGems < 1.2 cannot load specification versions > 2. CURRENT_SPECIFICATION_VERSION = 4 # :nodoc: ## # An informal list of changes to the specification. The highest-valued # key should be equal to the CURRENT_SPECIFICATION_VERSION. SPECIFICATION_VERSION_HISTORY = { # :nodoc: -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]"', ], 2 => [ 'Added "required_rubygems_version"', 'Now forward-compatible with future versions', ], 3 => [ 'Added Fixnum validation to the specification_version', ], 4 => [ 'Added sandboxed freeform metadata to the specification version.', ], }.freeze MARSHAL_FIELDS = { # :nodoc: -1 => 16, 1 => 16, 2 => 16, 3 => 17, 4 => 18, }.freeze today = Time.now.utc TODAY = Time.utc(today.year, today.month, today.day) # :nodoc: @load_cache = {} # :nodoc: @load_cache_mutex = Thread::Mutex.new VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc: # :startdoc: ## # List of attribute names: [:name, :version, ...] @@required_attributes = [:rubygems_version, :specification_version, :name, :version, :date, :summary, :require_paths] ## # Map of attribute names to default values. @@default_value = { :authors => [], :autorequire => nil, :bindir => 'bin', :cert_chain => [], :date => nil, :dependencies => [], :description => nil, :email => nil, :executables => [], :extensions => [], :extra_rdoc_files => [], :files => [], :homepage => nil, :licenses => [], :metadata => {}, :name => nil, :platform => Gem::Platform::RUBY, :post_install_message => nil, :rdoc_options => [], :require_paths => ['lib'], :required_ruby_version => Gem::Requirement.default, :required_rubygems_version => Gem::Requirement.default, :requirements => [], :rubygems_version => Gem::VERSION, :signing_key => nil, :specification_version => CURRENT_SPECIFICATION_VERSION, :summary => nil, :test_files => [], :version => nil, }.freeze # rubocop:disable Style/MutableConstant INITIALIZE_CODE_FOR_DEFAULTS = { } # :nodoc: # rubocop:enable Style/MutableConstant @@default_value.each do |k,v| INITIALIZE_CODE_FOR_DEFAULTS[k] = case v when [], {}, true, false, nil, Numeric, Symbol v.inspect when String v.dump when Numeric "default_value(:#{k})" else "default_value(:#{k}).dup" end end @@attributes = @@default_value.keys.sort_by {|s| s.to_s } @@array_attributes = @@default_value.reject {|k,v| v != [] }.keys @@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition do |k| @@default_value[k].nil? end def self.clear_specs # :nodoc: @@all_specs_mutex.synchronize do @@all = nil @@stubs = nil @@stubs_by_name = {} @@spec_with_requirable_file = {} @@active_stub_with_requirable_file = {} end end private_class_method :clear_specs @@all_specs_mutex = Thread::Mutex.new clear_specs # Sentinel object to represent "not found" stubs NOT_FOUND = Struct.new(:to_spec, :this).new # :nodoc: # Tracking removed method calls to warn users during build time. REMOVED_METHODS = [:rubyforge_project=].freeze # :nodoc: def removed_method_calls @removed_method_calls ||= [] end ###################################################################### # :section: Required gemspec attributes ## # This gem's name. # # Usage: # # spec.name = 'rake' attr_accessor :name ## # This gem's version. # # The version string can contain numbers and periods, such as +1.0.0+. # A gem is a 'prerelease' gem if the version has a letter in it, such as # +1.0.0.pre+. # # Usage: # # spec.version = '0.4.1' attr_reader :version ## # A short summary of this gem's description. Displayed in `gem list -d`. # # The #description should be more detailed than the summary. # # Usage: # # spec.summary = "This is a small summary of my gem" attr_reader :summary ## # 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. # # Usage: # # require 'rake' # spec.files = FileList['lib/**/*.rb', # 'bin/*', # '[A-Z]*'].to_a # # # or without Rake... # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*'] # spec.files += Dir['[A-Z]*'] # spec.files.reject! { |fn| fn.include? "CVS" } def files # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks) # DOC: Why isn't it normal? Why does it suck? How can we fix this? @files = [@files, @test_files, add_bindir(@executables), @extra_rdoc_files, @extensions, ].flatten.compact.uniq.sort end ## # A list of authors for this gem. # # Alternatively, a single author can be specified by assigning a string to # `spec.author` # # Usage: # # spec.authors = ['John Jones', 'Mary Smith'] def authors=(value) @authors = Array(value).flatten.grep(String) end ###################################################################### # :section: Recommended gemspec attributes ## # The version of Ruby required by this gem # # Usage: # # spec.required_ruby_version = '>= 2.7.0' attr_reader :required_ruby_version ## # A long description of this gem # # The description should be more detailed than the summary but not # excessively long. A few paragraphs is a recommended length with no # examples or formatting. # # Usage: # # spec.description = <<-EOF # Rake is a Make-like program implemented in Ruby. Tasks and # dependencies are specified in standard Ruby syntax. # EOF attr_reader :description ## # A contact email address (or addresses) for this gem # # Usage: # # spec.email = 'john.jones@example.com' # spec.email = ['jack@example.com', 'jill@example.com'] attr_accessor :email ## # The URL of this gem's home page # # Usage: # # spec.homepage = 'https://github.com/ruby/rake' attr_accessor :homepage ## # The license for this gem. # # The license must be no more than 64 characters. # # This should just be the name of your license. The full text of the license # should be inside of the gem (at the top level) when you build it. # # The simplest way is to specify the standard SPDX ID # https://spdx.org/licenses/ for the license. # Ideally, you should pick one that is OSI (Open Source Initiative) # http://opensource.org/licenses/alphabetical approved. # # The most commonly used OSI-approved licenses are MIT and Apache-2.0. # GitHub also provides a license picker at http://choosealicense.com/. # # You can also use a custom license file along with your gemspec and specify # a LicenseRef-<idstring>, where idstring is the name of the file containing # the license text. # # You should specify a license for your gem so that people know how they are # permitted to use it and any restrictions you're placing on it. Not # specifying a license means all rights are reserved; others have no right # to use the code for any purpose. # # You can set multiple licenses with #licenses= # # Usage: # spec.license = 'MIT' def license=(o) self.licenses = [o] end ## # The license(s) for the library. # # Each license must be a short name, no more than 64 characters. # # This should just be the name of your license. The full # text of the license should be inside of the gem when you build it. # # See #license= for more discussion # # Usage: # spec.licenses = ['MIT', 'GPL-2.0'] def licenses=(licenses) @licenses = Array licenses end ## # The metadata holds extra data for this gem that may be useful to other # consumers and is settable by gem authors. # # Metadata items have the following restrictions: # # * The metadata must be a Hash object # * All keys and values must be Strings # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024 # bytes # * All strings must be UTF-8, no binary data is allowed # # You can use metadata to specify links to your gem's homepage, codebase, # documentation, wiki, mailing list, issue tracker and changelog. # # s.metadata = { # "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", # "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", # "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", # "homepage_uri" => "https://bestgemever.example.io", # "mailing_list_uri" => "https://groups.example.com/bestgemever", # "source_code_uri" => "https://example.com/user/bestgemever", # "wiki_uri" => "https://example.com/user/bestgemever/wiki" # "funding_uri" => "https://example.com/donate" # } # # These links will be used on your gem's page on rubygems.org and must pass # validation against following regex. # # %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z} attr_accessor :metadata ###################################################################### # :section: Optional gemspec attributes ## # Singular (alternative) writer for #authors # # Usage: # # spec.author = 'John Jones' def author=(o) self.authors = [o] end ## # The path in the gem for executable scripts. Usually 'bin' # # Usage: # # spec.bindir = 'bin' attr_accessor :bindir ## # The certificate chain used to sign this gem. See Gem::Security for # details. attr_accessor :cert_chain ## # A message that gets displayed after the gem is installed. # # Usage: # # spec.post_install_message = "Thanks for installing!" attr_accessor :post_install_message ## # The platform this gem runs on. # # This is usually Gem::Platform::RUBY or Gem::Platform::CURRENT. # # Most gems contain pure Ruby code; they should simply leave the default # value in place. Some gems contain C (or other) code to be compiled into a # Ruby "extension". The gem should leave the default value in place unless # the code will only compile on a certain type of system. Some gems consist # of pre-compiled code ("binary gems"). It's especially important that they # set the platform attribute appropriately. A shortcut is to set the # platform to Gem::Platform::CURRENT, which will cause the gem builder to set # the platform to the appropriate value for the system on which the build is # being performed. # # If this attribute is set to a non-default value, it will be included in # the filename of the gem when it is built such as: # nokogiri-1.6.0-x86-mingw32.gem # # Usage: # # spec.platform = Gem::Platform.local def platform=(platform) if @original_platform.nil? or @original_platform == Gem::Platform::RUBY @original_platform = platform end case platform when Gem::Platform::CURRENT then @new_platform = Gem::Platform.local @original_platform = @new_platform.to_s when Gem::Platform then @new_platform = platform # legacy constants when nil, Gem::Platform::RUBY then @new_platform = Gem::Platform::RUBY when 'mswin32' then # was Gem::Platform::WIN32 @new_platform = Gem::Platform.new 'x86-mswin32' when 'i586-linux' then # was Gem::Platform::LINUX_586 @new_platform = Gem::Platform.new 'x86-linux' when 'powerpc-darwin' then # was Gem::Platform::DARWIN @new_platform = Gem::Platform.new 'ppc-darwin' else @new_platform = Gem::Platform.new platform end @platform = @new_platform.to_s invalidate_memoized_attributes @new_platform end ## # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is # activated. #-- # See also #require_paths #++ # If you have an extension you do not need to add <code>"ext"</code> to the # require path, the extension build process will copy the extension files # into "lib" for you. # # The default value is <code>"lib"</code> # # Usage: # # # If all library files are in the root directory... # spec.require_paths = ['.'] def require_paths=(val) @require_paths = Array(val) end ## # The RubyGems version required by this gem attr_reader :required_rubygems_version ## # The version of RubyGems used to create this gem. # # Do not set this, it is set automatically when the gem is packaged. attr_accessor :rubygems_version ## # The key used to sign this gem. See Gem::Security for details. attr_accessor :signing_key ## # Adds a development dependency named +gem+ with +requirements+ to this # gem. # # Usage: # # spec.add_development_dependency 'example', '~> 1.1', '>= 1.1.4' # # Development dependencies aren't installed by default and aren't # activated when a gem is required. 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. # # Usage: # # spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4' def add_runtime_dependency(gem, *requirements) if requirements.uniq.size != requirements.size warn "WARNING: duplicated #{gem} dependency #{requirements}" end add_dependency_with_type(gem, :runtime, requirements) end ## # Executables included in the gem. # # For example, the rake gem has rake as an executable. You don’t specify the # full path (as in bin/rake); all application-style files are expected to be # found in bindir. These files must be executable Ruby files. Files that # use bash or other interpreters will not work. # # Executables included may only be ruby scripts, not scripts for other # languages or compiled binaries. # # Usage: # # spec.executables << 'rake' def executables @executables ||= [] end ## # Extensions to build when installing the gem, specifically the paths to # extconf.rb-style files used to compile extensions. # # These files will be run when the gem is installed, causing the C (or # whatever) code to be compiled on the user’s machine. # # Usage: # # spec.extensions << 'ext/rmagic/extconf.rb' # # See Gem::Ext::Builder for information about writing extensions for gems. def extensions @extensions ||= [] end ## # Extra files to add to RDoc such as README or doc/examples.txt # # When the user elects to generate the RDoc documentation for a gem (typically # at install time), all the library files are sent to RDoc for processing. # This option allows you to have some non-code files included for a more # complete set of documentation. # # Usage: # # spec.extra_rdoc_files = ['README', 'doc/user-guide.txt'] def extra_rdoc_files @extra_rdoc_files ||= [] end ## # The version of RubyGems that installed this gem. Returns # <code>Gem::Version.new(0)</code> for gems installed by versions earlier # than RubyGems 2.2.0. def installed_by_version # :nodoc: @installed_by_version ||= Gem::Version.new(0) end ## # Sets the version of RubyGems that installed this gem. See also # #installed_by_version. def installed_by_version=(version) # :nodoc: @installed_by_version = Gem::Version.new version end ## # Specifies the rdoc options to be used when generating API documentation. # # Usage: # # spec.rdoc_options << '--title' << 'Rake -- Ruby Make' << # '--main' << 'README' << # '--line-numbers' def rdoc_options @rdoc_options ||= [] end ## # The version of Ruby required by this gem. The ruby version can be # specified to the patch-level: # # $ ruby -v -e 'p Gem.ruby_version' # ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0] # #<Gem::Version "2.0.0.247"> # # Prereleases can also be specified. # # Usage: # # # This gem will work with 1.8.6 or greater... # spec.required_ruby_version = '>= 1.8.6' # # # Only with final releases of major version 2 where minor version is at least 3 # spec.required_ruby_version = '~> 2.3' # # # Only prereleases or final releases after 2.6.0.preview2 # spec.required_ruby_version = '> 2.6.0.preview2' # # # This gem will work with 2.3.0 or greater, including major version 3, but lesser than 4.0.0 # spec.required_ruby_version = '>= 2.3', '< 4' def required_ruby_version=(req) @required_ruby_version = Gem::Requirement.create req end ## # The RubyGems version required by this gem def required_rubygems_version=(req) @required_rubygems_version = Gem::Requirement.create req end ## # Lists the external (to RubyGems) requirements that must be met for this gem # to work. It's simply information for the user. # # Usage: # # spec.requirements << 'libmagick, v6.0' # spec.requirements << 'A good graphics card' def requirements @requirements ||= [] end ## # A collection of unit test files. They will be loaded as unit tests when # the user requests a gem to be unit tested. # # Usage: # spec.test_files = Dir.glob('test/tc_*.rb') # spec.test_files = ['tests/test-suite.rb'] def test_files=(files) # :nodoc: @test_files = Array files end ###################################################################### # :section: Specification internals ## # True when this gemspec has been activated. This attribute is not persisted. attr_accessor :activated alias :activated? :activated ## # Autorequire was used by old RubyGems to automatically require a file. # # Deprecated: It is neither supported nor functional. attr_accessor :autorequire # :nodoc: ## # Sets the default executable for this gem. # # Deprecated: You must now specify the executable name to Gem.bin_path. attr_writer :default_executable rubygems_deprecate :default_executable= ## # Allows deinstallation of gems with legacy platforms. attr_writer :original_platform # :nodoc: ## # The Gem::Specification version of this gemspec. # # Do not set this, it is set automatically when the gem is packaged. attr_accessor :specification_version def self._all # :nodoc: @@all_specs_mutex.synchronize { @@all ||= Gem.loaded_specs.values | stubs.map(&:to_spec) } end def self.clear_load_cache # :nodoc: @load_cache_mutex.synchronize do @load_cache.clear end end private_class_method :clear_load_cache def self.each_gemspec(dirs) # :nodoc: dirs.each do |dir| Gem::Util.glob_files_in_dir("*.gemspec", dir).each do |path| yield path.tap(&Gem::UNTAINT) end end end def self.gemspec_stubs_in(dir, pattern) Gem::Util.glob_files_in_dir(pattern, dir).map {|path| yield path }.select(&:valid?) end private_class_method :gemspec_stubs_in def self.installed_stubs(dirs, pattern) map_stubs(dirs, pattern) do |path, base_dir, gems_dir| Gem::StubSpecification.gemspec_stub(path, base_dir, gems_dir) end end private_class_method :installed_stubs def self.map_stubs(dirs, pattern) # :nodoc: dirs.flat_map do |dir| base_dir = File.dirname dir gems_dir = File.join base_dir, "gems" gemspec_stubs_in(dir, pattern) {|path| yield path, base_dir, gems_dir } end end private_class_method :map_stubs def self.each_spec(dirs) # :nodoc: each_gemspec(dirs) do |path| spec = self.load path yield spec if spec end end ## # Returns a Gem::StubSpecification for every installed gem def self.stubs @@stubs ||= begin pattern = "*.gemspec" stubs = stubs_for_pattern(pattern, false) @@stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name) stubs end end ## # Returns a Gem::StubSpecification for default gems def self.default_stubs(pattern = "*.gemspec") base_dir = Gem.default_dir gems_dir = File.join base_dir, "gems" gemspec_stubs_in(Gem.default_specifications_dir, pattern) do |path| Gem::StubSpecification.default_gemspec_stub(path, base_dir, gems_dir) end end ## # Returns a Gem::StubSpecification for installed gem named +name+ # only returns stubs that match Gem.platforms def self.stubs_for(name) if @@stubs @@stubs_by_name[name] || [] else @@stubs_by_name[name] ||= stubs_for_pattern("#{name}-*.gemspec").select do |s| s.name == name end end end ## # Finds stub specifications matching a pattern from the standard locations, # optionally filtering out specs not matching the current platform # def self.stubs_for_pattern(pattern, match_platform = true) # :nodoc: installed_stubs = installed_stubs(Gem::Specification.dirs, pattern) installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform stubs = installed_stubs + default_stubs(pattern) stubs = stubs.uniq {|stub| stub.full_name } _resort!(stubs) stubs end def self._resort!(specs) # :nodoc: specs.sort! do |a, b| names = a.name <=> b.name next names if names.nonzero? versions = b.version <=> a.version next versions if versions.nonzero? b.platform == Gem::Platform::RUBY ? -1 : 1 end end ## # Loads the default specifications. It should be called only once. def self.load_defaults each_spec([Gem.default_specifications_dir]) do |spec| # #load returns nil if the spec is bad, so we just ignore # it at this stage Gem.register_default_spec(spec) end end ## # Returns all specifications. This method is discouraged from use. # You probably want to use one of the Enumerable methods instead. def self.all warn "NOTE: Specification.all called from #{caller.first}" unless Gem::Deprecate.skip _all end ## # Sets the known specs to +specs+. Not guaranteed to work for you in # the future. Use at your own risk. Caveat emptor. Doomy doom doom. # Etc etc. # #-- # Makes +specs+ the known specs # Listen, time is a river # Winter comes, code breaks # # -- wilsonb def self.all=(specs) @@stubs_by_name = specs.group_by(&:name) @@all = @@stubs = specs end ## # Return full names of all specs in sorted order. def self.all_names self._all.map(&:full_name) end ## # Return the list of all array-oriented instance variables. #-- # Not sure why we need to use so much stupid reflection in here... def self.array_attributes @@array_attributes.dup end ## # Return the list of all instance variables. #-- # Not sure why we need to use so much stupid reflection in here... def self.attribute_names @@attributes.dup end ## # Return the directories that Specification uses to find specs. def self.dirs @@dirs ||= Gem.path.collect do |dir| File.join dir.dup.tap(&Gem::UNTAINT), "specifications" end end ## # Set the directories that Specification uses to find specs. Setting # this resets the list of known specs. def self.dirs=(dirs) self.reset @@dirs = Array(dirs).map {|dir| File.join dir, "specifications" } end extend Enumerable ## # Enumerate every known spec. See ::dirs= and ::add_spec to set the list of # specs. def self.each return enum_for(:each) unless block_given? self._all.each do |x| yield x end end ## # Returns every spec that matches +name+ and optional +requirements+. def self.find_all_by_name(name, *requirements) requirements = Gem::Requirement.default if requirements.empty? # TODO: maybe try: find_all { |s| spec === dep } Gem::Dependency.new(name, *requirements).matching_specs end ## # Returns every spec that has the given +full_name+ def self.find_all_by_full_name(full_name) stubs.select {|s| s.full_name == full_name }.map(&:to_spec) end ## # Find the best specification matching a +name+ and +requirements+. Raises # if the dependency doesn't resolve to a valid specification. def self.find_by_name(name, *requirements) requirements = Gem::Requirement.default if requirements.empty? # TODO: maybe try: find { |s| spec === dep } Gem::Dependency.new(name, *requirements).to_spec end ## # Return the best specification that contains the file matching +path+. def self.find_by_path(path) path = path.dup.freeze spec = @@spec_with_requirable_file[path] ||= (stubs.find do |s| next unless Gem::BundlerVersionFinder.compatible?(s) s.contains_requirable_file? path end || NOT_FOUND) spec.to_spec end ## # Return the best specification that contains the file matching +path+ # amongst the specs that are not activated. def self.find_inactive_by_path(path) stub = stubs.find do |s| next if s.activated? next unless Gem::BundlerVersionFinder.compatible?(s) s.contains_requirable_file? path end stub && stub.to_spec end def self.find_active_stub_by_path(path) stub = @@active_stub_with_requirable_file[path] ||= (stubs.find do |s| s.activated? and s.contains_requirable_file? path end || NOT_FOUND) stub.this end ## # Return currently unresolved specs that contain the file matching +path+. def self.find_in_unresolved(path) unresolved_specs.find_all {|spec| spec.contains_requirable_file? path } end ## # Search through all unresolved deps and sub-dependencies and return # specs that contain the file matching +path+. def self.find_in_unresolved_tree(path) unresolved_specs.each do |spec| spec.traverse do |from_spec, dep, to_spec, trail| if to_spec.has_conflicts? || to_spec.conficts_when_loaded_with?(trail) :next else return trail.reverse if to_spec.contains_requirable_file? path end end end [] end def self.unresolved_specs unresolved_deps.values.map {|dep| dep.to_specs }.flatten end private_class_method :unresolved_specs ## # 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. def self.from_yaml(input) Gem.load_yaml input = normalize_yaml_input input spec = Gem::SafeYAML.safe_load input if spec && spec.class == FalseClass raise Gem::EndOfYAMLException end unless Gem::Specification === spec raise Gem::Exception, "YAML data doesn't evaluate to gem specification" end spec.specification_version ||= NONEXISTENT_SPECIFICATION_VERSION spec.reset_nil_attributes_to_default spec end ## # Return the latest specs, optionally including prerelease specs if # +prerelease+ is true. def self.latest_specs(prerelease = false) _latest_specs Gem::Specification._all, prerelease end ## # Return the latest installed spec for gem +name+. def self.latest_spec_for(name) latest_specs(true).find {|installed_spec| installed_spec.name == name } end def self._latest_specs(specs, prerelease = false) # :nodoc: result = {} specs.reverse_each do |spec| next if spec.version.prerelease? unless prerelease result[spec.name] = spec end result.map(&:last).flatten.sort_by{|tup| tup.name } end ## # Loads Ruby format gemspec from +file+. def self.load(file) return unless file _spec = @load_cache_mutex.synchronize { @load_cache[file] } return _spec if _spec file = file.dup.tap(&Gem::UNTAINT) return unless File.file?(file) code = File.read file, :mode => 'r:UTF-8:-' code.tap(&Gem::UNTAINT) begin _spec = eval code, binding, file if Gem::Specification === _spec _spec.loaded_from = File.expand_path file.to_s @load_cache_mutex.synchronize do prev = @load_cache[file] if prev _spec = prev else @load_cache[file] = _spec end end return _spec end warn "[#{file}] isn't a Gem::Specification (#{_spec.class} instead)." rescue SignalException, SystemExit raise rescue SyntaxError, Exception => e warn "Invalid gemspec in [#{file}]: #{e}" end nil end ## # Specification attributes that must be non-nil def self.non_nil_attributes @@non_nil_attributes.dup end ## # Make sure the YAML specification is properly formatted with dashes def self.normalize_yaml_input(input) result = input.respond_to?(:read) ? input.read : input result = "--- " + result unless result.start_with?("--- ") result = result.dup result.gsub!(/ !!null \n/, " \n") # date: 2011-04-26 00:00:00.000000000Z # date: 2011-04-26 00:00:00.000000000 Z result.gsub!(/^(date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+?)Z/, '\1 Z') result end ## # Return a list of all outdated local gem names. This method is HEAVY # as it must go fetch specifications from the server. # # Use outdated_and_latest_version if you wish to retrieve the latest remote # version as well. def self.outdated outdated_and_latest_version.map {|local, _| local.name } end ## # Enumerates the outdated local gems yielding the local specification and # the latest remote version. # # This method may take some time to return as it must check each local gem # against the server's index. def self.outdated_and_latest_version return enum_for __method__ unless block_given? # TODO: maybe we should switch to rubygems' version service? fetcher = Gem::SpecFetcher.fetcher latest_specs(true).each do |local_spec| dependency = Gem::Dependency.new local_spec.name, ">= #{local_spec.version}" remotes, = fetcher.search_for_dependency dependency remotes = remotes.map {|n, _| n.version } latest_remote = remotes.sort.last yield [local_spec, latest_remote] if latest_remote and local_spec.version < latest_remote end nil end ## # Is +name+ a required attribute? def self.required_attribute?(name) @@required_attributes.include? name.to_sym end ## # Required specification attributes def self.required_attributes @@required_attributes.dup end ## # Reset the list of known specs, running pre and post reset hooks # registered in Gem. def self.reset @@dirs = nil Gem.pre_reset_hooks.each {|hook| hook.call } clear_specs clear_load_cache unresolved = unresolved_deps unless unresolved.empty? w = "W" + "ARN" warn "#{w}: Unresolved or ambiguous specs during Gem::Specification.reset:" unresolved.values.each do |dep| warn " #{dep}" versions = find_all_by_name(dep.name) unless versions.empty? warn " Available/installed versions of this gem:" versions.each {|s| warn " - #{s.version}" } end end warn "#{w}: Clearing out unresolved specs. Try 'gem cleanup <gem>'" warn "Please report a bug if this causes problems." unresolved.clear end Gem.post_reset_hooks.each {|hook| hook.call } end # DOC: This method needs documented or nodoc'd def self.unresolved_deps @unresolved_deps ||= Hash.new {|h, n| h[n] = Gem::Dependency.new n } end ## # Load custom marshal format, re-initializing defaults as needed def self._load(str) Gem.load_yaml 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 spec.instance_variable_set :@specification_version, current_version MARSHAL_FIELDS[current_version] else MARSHAL_FIELDS[spec.specification_version] end if array.size < field_count raise TypeError, "invalid Gem::Specification format #{array.inspect}" end # Cleanup any YAML::PrivateType. They only show up for an old bug # where nil => null, so just convert them to nil based on the type. array.map! {|e| e.kind_of?(YAML::PrivateType) ? nil : e } 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.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] # offset due to rubyforge_project removal 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 :@metadata, array[18] spec.instance_variable_set :@loaded, false spec.instance_variable_set :@activated, false spec end def <=>(other) # :nodoc: sort_obj <=> other.sort_obj end def ==(other) # :nodoc: self.class === other && name == other.name && version == other.version && platform == other.platform end ## # Dump only crucial instance variables. #-- # MAINTAIN ORDER! # (down with the man) def _dump(limit) Marshal.dump [ @rubygems_version, @specification_version, @name, @version, date, @summary, @required_ruby_version, @required_rubygems_version, @original_platform, @dependencies, '', # rubyforge_project @email, @authors, @description, @homepage, true, # has_rdoc @new_platform, @licenses, @metadata, ] end ## # Activate this spec, registering it as a loaded spec and adding # it's lib paths to $LOAD_PATH. Returns true if the spec was # activated, false if it was previously activated. Freaks out if # there are conflicts upon activation. def activate other = Gem.loaded_specs[self.name] if other check_version_conflict other return false end raise_if_conflicts activate_dependencies add_self_to_load_path Gem.loaded_specs[self.name] = self @activated = true @loaded = true return true end ## # Activate all unambiguously resolved runtime dependencies of this # spec. Add any ambiguous dependencies to the unresolved list to be # resolved later, as needed. def activate_dependencies unresolved = Gem::Specification.unresolved_deps self.runtime_dependencies.each do |spec_dep| if loaded = Gem.loaded_specs[spec_dep.name] next if spec_dep.matches_spec? loaded msg = "can't satisfy '#{spec_dep}', already activated '#{loaded.full_name}'" e = Gem::LoadError.new msg e.name = spec_dep.name raise e end begin specs = spec_dep.to_specs rescue Gem::MissingSpecError => e raise Gem::MissingSpecError.new(e.name, e.requirement, "at: #{self.spec_file}") end if specs.size == 1 specs.first.activate else name = spec_dep.name unresolved[name] = unresolved[name].merge spec_dep end end unresolved.delete self.name end ## # Abbreviate the spec for downloading. Abbreviated specs are only used for # searching, downloading and related activities and do not need deployment # specific information (e.g. list of files). So we abbreviate the spec, # making it much smaller for quicker downloads. def abbreviate self.files = [] self.test_files = [] self.rdoc_options = [] self.extra_rdoc_files = [] self.cert_chain = [] end ## # Sanitize the descriptive fields in the spec. Sometimes non-ASCII # characters will garble the site index. Non-ASCII characters will # be replaced by their XML entity equivalent. def sanitize self.summary = sanitize_string(summary) self.description = sanitize_string(description) self.post_install_message = sanitize_string(post_install_message) self.authors = authors.collect {|a| sanitize_string(a) } end ## # Sanitize a single string. def sanitize_string(string) return string unless string # HACK the #to_s is in here because RSpec has an Array of Arrays of # Strings for authors. Need a way to disallow bad values on gemspec # generation. (Probably won't happen.) string.to_s end ## # Returns an array with bindir attached to each executable in the # +executables+ list def add_bindir(executables) return nil if executables.nil? if @bindir Array(executables).map {|e| File.join(@bindir, e) } else executables end rescue return nil end ## # Adds a dependency on gem +dependency+ with type +type+ that requires # +requirements+. Valid types are currently <tt>:runtime</tt> and # <tt>:development</tt>. def add_dependency_with_type(dependency, type, requirements) requirements = if requirements.empty? Gem::Requirement.default else requirements.flatten end unless dependency.respond_to?(:name) && dependency.respond_to?(:requirement) dependency = Gem::Dependency.new(dependency.to_s, requirements, type) end dependencies << dependency end private :add_dependency_with_type alias add_dependency add_runtime_dependency ## # Adds this spec's require paths to LOAD_PATH, in the proper location. def add_self_to_load_path return if default_gem? paths = full_require_paths Gem.add_to_load_path(*paths) end ## # Singular reader for #authors. Returns the first author in the list def author val = authors and val.first end ## # The list of author names who wrote this gem. # # spec.authors = ['Chad Fowler', 'Jim Weirich', 'Rich Kilmer'] def authors @authors ||= [] end ## # Returns the full path to installed gem's bin directory. # # NOTE: do not confuse this with +bindir+, which is just 'bin', not # a full path. def bin_dir @bin_dir ||= File.join gem_dir, bindir end ## # Returns the full path to an executable named +name+ in this gem. def bin_file(name) File.join bin_dir, name end ## # Returns the build_args used to install the gem def build_args if File.exist? build_info_file build_info = File.readlines build_info_file build_info = build_info.map {|x| x.strip } build_info.delete "" build_info else [] end end ## # Builds extensions for this platform if the gem has extensions listed and # the gem.build_complete file is missing. def build_extensions # :nodoc: return if extensions.empty? return if default_gem? return if File.exist? gem_build_complete_path return if !File.writable?(base_dir) return if !File.exist?(File.join(base_dir, 'extensions')) begin # We need to require things in $LOAD_PATH without looking for the # extension we are about to build. unresolved_deps = Gem::Specification.unresolved_deps.dup Gem::Specification.unresolved_deps.clear require_relative 'config_file' require_relative 'ext' require_relative 'user_interaction' ui = Gem::SilentUI.new Gem::DefaultUserInteraction.use_ui ui do builder = Gem::Ext::Builder.new self builder.build_extensions end ensure ui.close if ui Gem::Specification.unresolved_deps.replace unresolved_deps end end ## # Returns the full path to the build info directory def build_info_dir File.join base_dir, "build_info" end ## # Returns the full path to the file containing the build # information generated when the gem was installed def build_info_file File.join build_info_dir, "#{full_name}.info" end ## # Returns the full path to the cache directory containing this # spec's cached gem. def cache_dir @cache_dir ||= File.join base_dir, "cache" end ## # Returns the full path to the cached gem for this spec. def cache_file @cache_file ||= File.join cache_dir, "#{full_name}.gem" end ## # Return any possible conflicts against the currently loaded specs. def conflicts conflicts = {} self.runtime_dependencies.each do |dep| spec = Gem.loaded_specs[dep.name] if spec and not spec.satisfies_requirement? dep (conflicts[spec] ||= []) << dep end end env_req = Gem.env_requirement(name) (conflicts[self] ||= []) << env_req unless env_req.satisfied_by? version conflicts end ## # return true if there will be conflict when spec if loaded together with the list of specs. def conficts_when_loaded_with?(list_of_specs) # :nodoc: result = list_of_specs.any? do |spec| spec.dependencies.any? {|dep| dep.runtime? && (dep.name == name) && !satisfies_requirement?(dep) } end result end ## # Return true if there are possible conflicts against the currently loaded specs. def has_conflicts? return true unless Gem.env_requirement(name).satisfied_by?(version) self.dependencies.any? do |dep| if dep.runtime? spec = Gem.loaded_specs[dep.name] spec and not spec.satisfies_requirement? dep else false end end end # The date this gem was created. # # If SOURCE_DATE_EPOCH is set as an environment variable, use that to support # reproducible builds; otherwise, default to the current UTC date. # # Details on SOURCE_DATE_EPOCH: # https://reproducible-builds.org/specs/source-date-epoch/ def date @date ||= Time.utc(*Gem.source_date_epoch.utc.to_a[3..5].reverse) end DateLike = Object.new # :nodoc: def DateLike.===(obj) # :nodoc: defined?(::Date) and Date === obj end DateTimeFormat = # :nodoc: /\A (\d{4})-(\d{2})-(\d{2}) (\s+ \d{2}:\d{2}:\d{2}\.\d+ \s* (Z | [-+]\d\d:\d\d) )? \Z/x.freeze ## # The date this gem was created # # DO NOT set this, it is set automatically when the gem is packaged. def date=(date) # We want to end up with a Time object with one-day resolution. # This is the cleanest, most-readable, faster-than-using-Date # way to do it. @date = case date when String then if DateTimeFormat =~ date Time.utc($1.to_i, $2.to_i, $3.to_i) else raise(Gem::InvalidSpecificationException, "invalid date format in specification: #{date.inspect}") end when Time, DateLike then Time.utc(date.year, date.month, date.day) else TODAY end end ## # The default executable for this gem. # # Deprecated: The name of the gem is assumed to be the name of the # executable now. See Gem.bin_path. def default_executable # :nodoc: if defined?(@default_executable) and @default_executable result = @default_executable elsif @executables and @executables.size == 1 result = Array(@executables).first else result = nil end result end rubygems_deprecate :default_executable ## # The default value for specification attribute +name+ def default_value(name) @@default_value[name] end ## # A list of Gem::Dependency objects this gem depends on. # # Use #add_dependency or #add_development_dependency to add dependencies to # a gem. def dependencies @dependencies ||= [] 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]] def dependent_gems(check_dev=true) out = [] Gem::Specification.each do |spec| deps = check_dev ? spec.dependencies : spec.runtime_dependencies deps.each do |dep| if self.satisfies_requirement?(dep) sats = [] find_all_satisfiers(dep) do |sat| sats << sat end out << [spec, dep, sats] end end end out end ## # Returns all specs that matches this spec's runtime dependencies. def dependent_specs runtime_dependencies.map {|dep| dep.to_specs }.flatten end ## # A detailed description of this gem. See also #summary def description=(str) @description = str.to_s end ## # List of dependencies that are used for development def development_dependencies dependencies.select {|d| d.type == :development } end ## # Returns the full path to this spec's documentation directory. If +type+ # is given it will be appended to the end. For example: # # spec.doc_dir # => "/path/to/gem_repo/doc/a-1" # # spec.doc_dir 'ri' # => "/path/to/gem_repo/doc/a-1/ri" def doc_dir(type = nil) @doc_dir ||= File.join base_dir, 'doc', full_name if type File.join @doc_dir, type else @doc_dir end end def encode_with(coder) # :nodoc: mark_version coder.add 'name', @name coder.add 'version', @version platform = case @original_platform when nil, '' then 'ruby' when String then @original_platform else @original_platform.to_s end coder.add 'platform', platform attributes = @@attributes.map(&:to_s) - %w[name version platform] attributes.each do |name| coder.add name, instance_variable_get("@#{name}") end end def eql?(other) # :nodoc: self.class === other && same_attributes?(other) end ## # Singular accessor for #executables def executable val = executables and val.first end ## # Singular accessor for #executables def executable=(o) self.executables = [o] end ## # Sets executables to +value+, ensuring it is an array. def executables=(value) @executables = Array(value) end ## # Sets extensions to +extensions+, ensuring it is an array. def extensions=(extensions) @extensions = Array extensions end ## # Sets extra_rdoc_files to +files+, ensuring it is an array. def extra_rdoc_files=(files) @extra_rdoc_files = Array files end ## # The default (generated) file name of the gem. See also #spec_name. # # spec.file_name # => "example-1.0.gem" def file_name "#{full_name}.gem" end ## # Sets files to +files+, ensuring it is an array. def files=(files) @files = Array files end ## # Finds all gems that satisfy +dep+ def find_all_satisfiers(dep) Gem::Specification.each do |spec| yield spec if spec.satisfies_requirement? dep end end private :find_all_satisfiers ## # Creates a duplicate spec without large blobs that aren't used at runtime. def for_cache spec = dup spec.files = nil spec.test_files = nil spec end def full_name @full_name ||= super end ## # Work around bundler removing my methods def gem_dir # :nodoc: super end def gems_dir @gems_dir ||= File.join(base_dir, "gems") end ## # Deprecated and ignored, defaults to true. # # Formerly used to indicate this gem was RDoc-capable. def has_rdoc # :nodoc: true end rubygems_deprecate :has_rdoc ## # Deprecated and ignored. # # Formerly used to indicate this gem was RDoc-capable. def has_rdoc=(ignored) # :nodoc: @has_rdoc = true end rubygems_deprecate :has_rdoc= alias :has_rdoc? :has_rdoc # :nodoc: rubygems_deprecate :has_rdoc? ## # True if this gem has files in test_files def has_unit_tests? # :nodoc: not test_files.empty? end # :stopdoc: alias has_test_suite? has_unit_tests? # :startdoc: def hash # :nodoc: name.hash ^ version.hash end def init_with(coder) # :nodoc: @installed_by_version ||= nil yaml_initialize coder.tag, coder.map end eval <<-RUBY, binding, __FILE__, __LINE__ + 1 # frozen_string_literal: true def set_nil_attributes_to_nil #{@@nil_attributes.map {|key| "@#{key} = nil" }.join "; "} end private :set_nil_attributes_to_nil def set_not_nil_attributes_to_default_values #{@@non_nil_attributes.map {|key| "@#{key} = #{INITIALIZE_CODE_FOR_DEFAULTS[key]}" }.join ";"} end private :set_not_nil_attributes_to_default_values RUBY ## # Specification constructor. Assigns the default values to the attributes # and yields itself for further initialization. Optionally takes +name+ and # +version+. def initialize(name = nil, version = nil) super() @gems_dir = nil @base_dir = nil @loaded = false @activated = false @loaded_from = nil @original_platform = nil @installed_by_version = nil set_nil_attributes_to_nil set_not_nil_attributes_to_default_values @new_platform = Gem::Platform::RUBY self.name = name if name self.version = version if version if platform = Gem.platforms.last and platform != Gem::Platform::RUBY and platform != Gem::Platform.local self.platform = platform end yield self if block_given? end ## # Duplicates array_attributes from +other_spec+ so state isn't shared. def initialize_copy(other_spec) self.class.array_attributes.each do |name| name = :"@#{name}" next unless other_spec.instance_variable_defined? name begin val = other_spec.instance_variable_get(name) if val instance_variable_set name, val.dup elsif Gem.configuration.really_verbose warn "WARNING: #{full_name} has an invalid nil value for #{name}" end rescue TypeError e = Gem::FormatException.new \ "#{full_name} has an invalid value for #{name}" e.file_path = loaded_from raise e end end end def base_dir return Gem.dir unless loaded_from @base_dir ||= if default_gem? File.dirname File.dirname File.dirname loaded_from else File.dirname File.dirname loaded_from end end ## # Expire memoized instance variables that can incorrectly generate, replace # or miss files due changes in certain attributes used to compute them. def invalidate_memoized_attributes @full_name = nil @cache_file = nil end private :invalidate_memoized_attributes def inspect # :nodoc: if $DEBUG super else "#{super[0..-2]} #{full_name}>" end end ## # Files in the Gem under one of the require_paths def lib_files @files.select do |file| require_paths.any? do |path| file.start_with? path end end end ## # Singular accessor for #licenses def license licenses.first end ## # Plural accessor for setting licenses # # See #license= for details def licenses @licenses ||= [] end def internal_init # :nodoc: super @bin_dir = nil @cache_dir = nil @cache_file = nil @doc_dir = nil @ri_dir = nil @spec_dir = nil @spec_file = nil end ## # Sets the rubygems_version to the current RubyGems version. def mark_version @rubygems_version = Gem::VERSION end ## # Track removed method calls to warn about during build time. # Warn about unknown attributes while loading a spec. def method_missing(sym, *a, &b) # :nodoc: if REMOVED_METHODS.include?(sym) removed_method_calls << sym return end if @specification_version > CURRENT_SPECIFICATION_VERSION and sym.to_s.end_with?("=") warn "ignoring #{sym} loading #{full_name}" if $DEBUG else super end end ## # Is this specification missing its extensions? When this returns true you # probably want to build_extensions def missing_extensions? return false if extensions.empty? return false if default_gem? return false if File.exist? gem_build_complete_path true 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. def normalize if defined?(@extra_rdoc_files) and @extra_rdoc_files @extra_rdoc_files.uniq! @files ||= [] @files.concat(@extra_rdoc_files) end @files = @files.uniq if @files @extensions = @extensions.uniq if @extensions @test_files = @test_files.uniq if @test_files @executables = @executables.uniq if @executables @extra_rdoc_files = @extra_rdoc_files.uniq if @extra_rdoc_files end ## # Return a NameTuple that represents this Specification def name_tuple Gem::NameTuple.new name, version, original_platform end ## # Returns the full name (name-version) of this gemspec using the original # platform. For use with legacy gems. def original_name # :nodoc: if platform == Gem::Platform::RUBY or platform.nil? "#{@name}-#{@version}" else "#{@name}-#{@version}-#{@original_platform}" end end ## # Cruft. Use +platform+. def original_platform # :nodoc: @original_platform ||= platform end ## # The platform this gem runs on. See Gem::Platform for details. def platform @new_platform ||= Gem::Platform::RUBY end def pretty_print(q) # :nodoc: q.group 2, 'Gem::Specification.new do |s|', 'end' do q.breakable attributes = @@attributes - [:name, :version] attributes.unshift :installed_by_version attributes.unshift :version attributes.unshift :name attributes.each do |attr_name| current_value = self.send attr_name current_value = current_value.sort if %i[files test_files].include? attr_name if current_value != default_value(attr_name) or self.class.required_attribute? attr_name q.text "s.#{attr_name} = " if attr_name == :date current_value = current_value.utc q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})" else q.pp current_value end q.breakable end end end end ## # Raise an exception if the version of this spec conflicts with the one # that is already loaded (+other+) def check_version_conflict(other) # :nodoc: return if self.version == other.version # This gem is already loaded. If the currently loaded gem is not in the # list of candidate gems, then we have a version conflict. msg = "can't activate #{full_name}, already activated #{other.full_name}" e = Gem::LoadError.new msg e.name = self.name raise e end private :check_version_conflict ## # Check the spec for possible conflicts and freak out if there are any. def raise_if_conflicts # :nodoc: if has_conflicts? raise Gem::ConflictError.new self, conflicts end end ## # Sets rdoc_options to +value+, ensuring it is an array. def rdoc_options=(options) @rdoc_options = Array options end ## # Singular accessor for #require_paths def require_path val = require_paths and val.first end ## # Singular accessor for #require_paths def require_path=(path) self.require_paths = Array(path) end ## # Set requirements to +req+, ensuring it is an array. def requirements=(req) @requirements = Array req end def respond_to_missing?(m, include_private = false) # :nodoc: false end ## # Returns the full path to this spec's ri directory. def ri_dir @ri_dir ||= File.join base_dir, 'ri', full_name end ## # Return a string containing a Ruby code representation of the given # object. def ruby_code(obj) case obj when String then obj.dump + ".freeze" when Array then '[' + obj.map {|x| ruby_code x }.join(", ") + ']' when Hash then seg = obj.keys.sort.map {|k| "#{k.to_s.dump} => #{obj[k].to_s.dump}" } "{ #{seg.join(', ')} }" when Gem::Version then obj.to_s.dump when DateLike then obj.strftime('%Y-%m-%d').dump when Time then obj.strftime('%Y-%m-%d').dump 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 list = obj.as_list "Gem::Requirement.new(#{ruby_code(list.size == 1 ? obj.to_s : list)})" else raise Gem::Exception, "ruby_code case not handled: #{obj.class}" end end private :ruby_code ## # List of dependencies that will automatically be activated at runtime. def runtime_dependencies dependencies.select(&:runtime?) end ## # True if this gem has the same attributes as +other+. def same_attributes?(spec) @@attributes.all? {|name, default| self.send(name) == spec.send(name) } end private :same_attributes? ## # Checks if this specification meets the requirement of +dependency+. def satisfies_requirement?(dependency) return @name == dependency.name && dependency.requirement.satisfied_by?(@version) end ## # Returns an object you can use to sort specifications in #sort_by. def sort_obj [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1] end ## # Used by Gem::Resolver to order Gem::Specification objects def source # :nodoc: Gem::Source::Installed.new end ## # Returns the full path to the directory containing this spec's # gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications def spec_dir @spec_dir ||= File.join base_dir, "specifications" end ## # Returns the full path to this spec's gemspec file. # eg: /usr/local/lib/ruby/gems/1.8/specifications/mygem-1.0.gemspec def spec_file @spec_file ||= File.join spec_dir, "#{full_name}.gemspec" end ## # The default name of the gemspec. See also #file_name # # spec.spec_name # => "example-1.0.gemspec" def spec_name "#{full_name}.gemspec" end ## # A short summary of this gem's description. def summary=(str) @summary = str.to_s.strip. gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').gsub(/\n[ \t]*/, " ") # so. weird. end ## # Singular accessor for #test_files def test_file # :nodoc: val = test_files and val.first end ## # Singular mutator for #test_files def test_file=(file) # :nodoc: self.test_files = [file] end ## # Test files included in this gem. You cannot append to this accessor, you # must assign to it. def test_files # :nodoc: # Handle the possibility that we have @test_suite_file but not # @test_files. This will happen when an old gem is loaded via # YAML. if defined? @test_suite_file @test_files = [@test_suite_file].flatten @test_suite_file = nil end if defined?(@test_files) and @test_files @test_files else @test_files = [] end 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. def to_ruby mark_version result = [] result << "# -*- encoding: utf-8 -*-" result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{raw_require_paths.join("\0")}" result << "#{Gem::StubSpecification::PREFIX}#{extensions.join "\0"}" unless extensions.empty? 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 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=" if metadata and !metadata.empty? result << " s.metadata = #{ruby_code metadata} if s.respond_to? :metadata=" end result << " s.require_paths = #{ruby_code raw_require_paths}" handled = [ :dependencies, :name, :platform, :require_paths, :required_rubygems_version, :specification_version, :version, :has_rdoc, :default_executable, :metadata, :signing_key, ] @@attributes.each do |attr_name| next if handled.include? attr_name current_value = self.send(attr_name) if current_value != default_value(attr_name) || self.class.required_attribute?(attr_name) result << " s.#{attr_name} = #{ruby_code current_value}" end end if String === signing_key result << " s.signing_key = #{signing_key.dump}.freeze" end if @installed_by_version result << nil result << " s.installed_by_version = \"#{Gem::VERSION}\" if s.respond_to? :installed_by_version" end unless dependencies.empty? result << nil result << " if s.respond_to? :specification_version then" result << " s.specification_version = #{specification_version}" result << " end" result << nil result << " if s.respond_to? :add_runtime_dependency then" dependencies.each do |dep| req = dep.requirements_list.inspect dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>.freeze, #{req})" end result << " else" dependencies.each do |dep| version_reqs_param = dep.requirements_list.inspect result << " s.add_dependency(%q<#{dep.name}>.freeze, #{version_reqs_param})" end result << " end" end result << "end" result << nil result.join "\n" end ## # Returns a Ruby lighter-weight code representation of this specification, # used for indexing only. # # See #to_ruby. def to_ruby_for_cache for_cache.to_ruby end def to_s # :nodoc: "#<Gem::Specification name=#{@name} version=#{@version}>" end ## # Returns self def to_spec self end def to_yaml(opts = {}) # :nodoc: Gem.load_yaml # Because the user can switch the YAML engine behind our # back, we have to check again here to make sure that our # psych code was properly loaded, and load it if not. unless Gem.const_defined?(:NoAliasYAMLTree) require_relative 'psych_tree' end builder = Gem::NoAliasYAMLTree.create builder << self ast = builder.tree require 'stringio' io = StringIO.new io.set_encoding Encoding::UTF_8 Psych::Visitors::Emitter.new(io).accept(ast) io.string.gsub(/ !!null \n/, " \n") end ## # Recursively walk dependencies of this spec, executing the +block+ for each # hop. def traverse(trail = [], visited = {}, &block) trail.push(self) begin dependencies.each do |dep| next unless dep.runtime? dep.matching_specs(true).each do |dep_spec| next if visited.has_key?(dep_spec) visited[dep_spec] = true trail.push(dep_spec) begin result = block[self, dep, dep_spec, trail] ensure trail.pop end unless result == :next spec_name = dep_spec.name dep_spec.traverse(trail, visited, &block) unless trail.any? {|s| s.name == spec_name } end end end ensure trail.pop end 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.. def validate(packaging = true, strict = false) normalize validation_policy = Gem::SpecificationPolicy.new(self) validation_policy.packaging = packaging validation_policy.validate(strict) end def keep_only_files_and_directories @executables.delete_if {|x| File.directory?(File.join(@bindir, x)) } @extensions.delete_if {|x| File.directory?(x) && !File.symlink?(x) } @extra_rdoc_files.delete_if {|x| File.directory?(x) && !File.symlink?(x) } @files.delete_if {|x| File.directory?(x) && !File.symlink?(x) } @test_files.delete_if {|x| File.directory?(x) && !File.symlink?(x) } end def validate_metadata Gem::SpecificationPolicy.new(self).validate_metadata end rubygems_deprecate :validate_metadata def validate_dependencies Gem::SpecificationPolicy.new(self).validate_dependencies end rubygems_deprecate :validate_dependencies def validate_permissions Gem::SpecificationPolicy.new(self).validate_permissions end rubygems_deprecate :validate_permissions ## # Set the version to +version+, potentially also setting # required_rubygems_version if +version+ indicates it is a # prerelease. def version=(version) @version = Gem::Version.create(version) # skip to set required_ruby_version when pre-released rubygems. # It caused to raise CircularDependencyError if @version.prerelease? && (@name.nil? || @name.strip != "rubygems") self.required_rubygems_version = '> 1.3.1' end invalidate_memoized_attributes return @version end def stubbed? false end def yaml_initialize(tag, vals) # :nodoc: vals.each do |ivar, val| case ivar when "date" # Force Date to go through the extra coerce logic in date= self.date = val.tap(&Gem::UNTAINT) else instance_variable_set "@#{ivar}", val.tap(&Gem::UNTAINT) end end @original_platform = @platform # for backwards compatibility self.platform = Gem::Platform.new @platform end ## # Reset nil attributes to their default values to make the spec valid def reset_nil_attributes_to_default nil_attributes = self.class.non_nil_attributes.find_all do |name| !instance_variable_defined?("@#{name}") || instance_variable_get("@#{name}").nil? end nil_attributes.each do |attribute| default = self.default_value attribute value = case default when Time, Numeric, Symbol, true, false, nil then default else default.dup end instance_variable_set "@#{attribute}", value end @installed_by_version ||= nil end def raw_require_paths # :nodoc: @require_paths end end rubygems/rubygems/defaults.rb 0000644 00000016074 15102661023 0012376 0 ustar 00 # frozen_string_literal: true module Gem DEFAULT_HOST = "https://rubygems.org".freeze @post_install_hooks ||= [] @done_installing_hooks ||= [] @post_uninstall_hooks ||= [] @pre_uninstall_hooks ||= [] @pre_install_hooks ||= [] ## # An Array of the default sources that come with RubyGems def self.default_sources %w[https://rubygems.org/] end ## # Default spec directory path to be used if an alternate value is not # specified in the environment def self.default_spec_cache_dir default_spec_cache_dir = File.join Gem.user_home, '.gem', 'specs' unless File.exist?(default_spec_cache_dir) default_spec_cache_dir = File.join Gem.data_home, 'gem', 'specs' end default_spec_cache_dir end ## # Default home directory path to be used if an alternate value is not # specified in the environment def self.default_dir path = if defined? RUBY_FRAMEWORK_VERSION [ File.dirname(RbConfig::CONFIG['sitedir']), 'Gems', RbConfig::CONFIG['ruby_version_dir_name'] || RbConfig::CONFIG['ruby_version'] ] else [ RbConfig::CONFIG['rubylibprefix'], 'gems', RbConfig::CONFIG['ruby_version_dir_name'] || RbConfig::CONFIG['ruby_version'] ] end @default_dir ||= File.join(*path) end ## # Returns binary extensions dir for specified RubyGems base dir or nil # if such directory cannot be determined. # # By default, the binary extensions are located side by side with their # Ruby counterparts, therefore nil is returned def self.default_ext_dir_for(base_dir) nil end ## # Paths where RubyGems' .rb files and bin files are installed def self.default_rubygems_dirs nil # default to standard layout end ## # Path to specification files of default gems. def self.default_specifications_dir @default_specifications_dir ||= File.join(Gem.default_dir, "specifications", "default") end ## # Finds the user's home directory. #-- # Some comments from the ruby-talk list regarding finding the home # directory: # # I have HOME, USERPROFILE and HOMEDRIVE + HOMEPATH. Ruby seems # to be depending on HOME in those code samples. I propose that # it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at # least on Win32). #++ #-- # #++ def self.find_home Dir.home.dup rescue if Gem.win_platform? File.expand_path File.join(ENV['HOMEDRIVE'] || ENV['SystemDrive'], '/') else File.expand_path "/" end end private_class_method :find_home ## # The home directory for the user. def self.user_home @user_home ||= find_home.tap(&Gem::UNTAINT) end ## # Path for gems in the user's home directory def self.user_dir gem_dir = File.join(Gem.user_home, ".gem") gem_dir = File.join(Gem.data_home, "gem") unless File.exist?(gem_dir) parts = [gem_dir, ruby_engine] ruby_version_dir_name = RbConfig::CONFIG['ruby_version_dir_name'] || RbConfig::CONFIG['ruby_version'] parts << ruby_version_dir_name unless ruby_version_dir_name.empty? File.join parts end ## # The path to standard location of the user's configuration directory. def self.config_home @config_home ||= (ENV["XDG_CONFIG_HOME"] || File.join(Gem.user_home, '.config')) end ## # Finds the user's config file def self.find_config_file gemrc = File.join Gem.user_home, '.gemrc' if File.exist? gemrc gemrc else File.join Gem.config_home, "gem", "gemrc" end end ## # The path to standard location of the user's .gemrc file. def self.config_file @config_file ||= find_config_file.tap(&Gem::UNTAINT) end ## # The path to standard location of the user's cache directory. def self.cache_home @cache_home ||= (ENV["XDG_CACHE_HOME"] || File.join(Gem.user_home, '.cache')) end ## # The path to standard location of the user's data directory. def self.data_home @data_home ||= (ENV["XDG_DATA_HOME"] || File.join(Gem.user_home, '.local', 'share')) end ## # How String Gem paths should be split. Overridable for esoteric platforms. def self.path_separator File::PATH_SEPARATOR end ## # Default gem load path def self.default_path path = [] path << user_dir if user_home && File.exist?(user_home) path << default_dir path << vendor_dir if vendor_dir and File.directory? vendor_dir path end ## # Deduce Ruby's --program-prefix and --program-suffix from its install name def self.default_exec_format exec_format = RbConfig::CONFIG['ruby_install_name'].sub('ruby', '%s') rescue '%s' unless exec_format =~ /%s/ raise Gem::Exception, "[BUG] invalid exec_format #{exec_format.inspect}, no %s" end exec_format end ## # The default directory for binaries def self.default_bindir if defined? RUBY_FRAMEWORK_VERSION # mac framework support '/usr/local/bin' else # generic install RbConfig::CONFIG['bindir'] end end def self.ruby_engine RUBY_ENGINE end ## # The default signing key path def self.default_key_path default_key_path = File.join Gem.user_home, ".gem", "gem-private_key.pem" unless File.exist?(default_key_path) default_key_path = File.join Gem.data_home, "gem", "gem-private_key.pem" end default_key_path end ## # The default signing certificate chain path def self.default_cert_path default_cert_path = File.join Gem.user_home, ".gem", "gem-public_cert.pem" unless File.exist?(default_cert_path) default_cert_path = File.join Gem.data_home, "gem", "gem-public_cert.pem" end default_cert_path end ## # Install extensions into lib as well as into the extension directory. def self.install_extension_in_lib # :nodoc: true end ## # Directory where vendor gems are installed. def self.vendor_dir # :nodoc: if vendor_dir = ENV['GEM_VENDOR'] return vendor_dir.dup end return nil unless RbConfig::CONFIG.key? 'vendordir' File.join RbConfig::CONFIG['vendordir'], 'gems', RbConfig::CONFIG['ruby_version_dir_name'] || RbConfig::CONFIG['ruby_version'] end ## # Default options for gem commands for Ruby packagers. # # The options here should be structured as an array of string "gem" # command names as keys and a string of the default options as values. # # Example: # # def self.operating_system_defaults # { # 'install' => '--no-rdoc --no-ri --env-shebang', # 'update' => '--no-rdoc --no-ri --env-shebang' # } # end def self.operating_system_defaults {} end ## # Default options for gem commands for Ruby implementers. # # The options here should be structured as an array of string "gem" # command names as keys and a string of the default options as values. # # Example: # # def self.platform_defaults # { # 'install' => '--no-rdoc --no-ri --env-shebang', # 'update' => '--no-rdoc --no-ri --env-shebang' # } # end def self.platform_defaults {} end end rubygems/rubygems/tsort/lib/tsort.rb 0000644 00000036235 15102661023 0013664 0 ustar 00 # frozen_string_literal: true #-- # tsort.rb - provides a module for topological sorting and strongly connected components. #++ # # # Gem::TSort implements topological sorting using Tarjan's algorithm for # strongly connected components. # # Gem::TSort is designed to be able to be used with any object which can be # interpreted as a directed graph. # # Gem::TSort requires two methods to interpret an object as a graph, # tsort_each_node and tsort_each_child. # # * tsort_each_node is used to iterate for all nodes over a graph. # * tsort_each_child is used to iterate for child nodes of a given node. # # The equality of nodes are defined by eql? and hash since # Gem::TSort uses Hash internally. # # == A Simple Example # # The following example demonstrates how to mix the Gem::TSort module into an # existing class (in this case, Hash). Here, we're treating each key in # the hash as a node in the graph, and so we simply alias the required # #tsort_each_node method to Hash's #each_key method. For each key in the # hash, the associated value is an array of the node's child nodes. This # choice in turn leads to our implementation of the required #tsort_each_child # method, which fetches the array of child nodes and then iterates over that # array using the user-supplied block. # # require 'rubygems/tsort/lib/tsort' # # class Hash # include Gem::TSort # alias tsort_each_node each_key # def tsort_each_child(node, &block) # fetch(node).each(&block) # end # end # # {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort # #=> [3, 2, 1, 4] # # {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components # #=> [[4], [2, 3], [1]] # # == A More Realistic Example # # A very simple `make' like tool can be implemented as follows: # # require 'rubygems/tsort/lib/tsort' # # class Make # def initialize # @dep = {} # @dep.default = [] # end # # def rule(outputs, inputs=[], &block) # triple = [outputs, inputs, block] # outputs.each {|f| @dep[f] = [triple]} # @dep[triple] = inputs # end # # def build(target) # each_strongly_connected_component_from(target) {|ns| # if ns.length != 1 # fs = ns.delete_if {|n| Array === n} # raise Gem::TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}") # end # n = ns.first # if Array === n # outputs, inputs, block = n # inputs_time = inputs.map {|f| File.mtime f}.max # begin # outputs_time = outputs.map {|f| File.mtime f}.min # rescue Errno::ENOENT # outputs_time = nil # end # if outputs_time == nil || # inputs_time != nil && outputs_time <= inputs_time # sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i # block.call # end # end # } # end # # def tsort_each_child(node, &block) # @dep[node].each(&block) # end # include Gem::TSort # end # # def command(arg) # print arg, "\n" # system arg # end # # m = Make.new # m.rule(%w[t1]) { command 'date > t1' } # m.rule(%w[t2]) { command 'date > t2' } # m.rule(%w[t3]) { command 'date > t3' } # m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' } # m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' } # m.build('t5') # # == Bugs # # * 'tsort.rb' is wrong name because this library uses # Tarjan's algorithm for strongly connected components. # Although 'strongly_connected_components.rb' is correct but too long. # # == References # # R. E. Tarjan, "Depth First Search and Linear Graph Algorithms", # <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972. # module Gem module TSort class Cyclic < StandardError end # Returns a topologically sorted array of nodes. # The array is sorted from children to parents, i.e. # the first element has no child and the last node has no parent. # # If there is a cycle, Gem::TSort::Cyclic is raised. # # class G # include Gem::TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # p graph.tsort #=> [4, 2, 3, 1] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # p graph.tsort # raises Gem::TSort::Cyclic # def tsort each_node = method(:tsort_each_node) each_child = method(:tsort_each_child) Gem::TSort.tsort(each_node, each_child) end # Returns a topologically sorted array of nodes. # The array is sorted from children to parents, i.e. # the first element has no child and the last node has no parent. # # The graph is represented by _each_node_ and _each_child_. # _each_node_ should have +call+ method which yields for each node in the graph. # _each_child_ should have +call+ method which takes a node argument and yields for each child node. # # If there is a cycle, Gem::TSort::Cyclic is raised. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1] # # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic # def TSort.tsort(each_node, each_child) Gem::TSort.tsort_each(each_node, each_child).to_a end # The iterator version of the #tsort method. # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but # modification of _obj_ during the iteration may lead to unexpected results. # # #tsort_each returns +nil+. # If there is a cycle, Gem::TSort::Cyclic is raised. # # class G # include Gem::TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # graph.tsort_each {|n| p n } # #=> 4 # # 2 # # 3 # # 1 # def tsort_each(&block) # :yields: node each_node = method(:tsort_each_node) each_child = method(:tsort_each_child) Gem::TSort.tsort_each(each_node, each_child, &block) end # The iterator version of the Gem::TSort.tsort method. # # The graph is represented by _each_node_ and _each_child_. # _each_node_ should have +call+ method which yields for each node in the graph. # _each_child_ should have +call+ method which takes a node argument and yields for each child node. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # Gem::TSort.tsort_each(each_node, each_child) {|n| p n } # #=> 4 # # 2 # # 3 # # 1 # def TSort.tsort_each(each_node, each_child) # :yields: node return to_enum(__method__, each_node, each_child) unless block_given? Gem::TSort.each_strongly_connected_component(each_node, each_child) {|component| if component.size == 1 yield component.first else raise Cyclic.new("topological sort failed: #{component.inspect}") end } end # Returns strongly connected components as an array of arrays of nodes. # The array is sorted from children to parents. # Each elements of the array represents a strongly connected component. # # class G # include Gem::TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # p graph.strongly_connected_components #=> [[4], [2], [3], [1]] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # p graph.strongly_connected_components #=> [[4], [2, 3], [1]] # def strongly_connected_components each_node = method(:tsort_each_node) each_child = method(:tsort_each_child) Gem::TSort.strongly_connected_components(each_node, each_child) end # Returns strongly connected components as an array of arrays of nodes. # The array is sorted from children to parents. # Each elements of the array represents a strongly connected component. # # The graph is represented by _each_node_ and _each_child_. # _each_node_ should have +call+ method which yields for each node in the graph. # _each_child_ should have +call+ method which takes a node argument and yields for each child node. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p Gem::TSort.strongly_connected_components(each_node, each_child) # #=> [[4], [2], [3], [1]] # # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # p Gem::TSort.strongly_connected_components(each_node, each_child) # #=> [[4], [2, 3], [1]] # def TSort.strongly_connected_components(each_node, each_child) Gem::TSort.each_strongly_connected_component(each_node, each_child).to_a end # The iterator version of the #strongly_connected_components method. # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to # <tt><em>obj</em>.strongly_connected_components.each</tt>, but # modification of _obj_ during the iteration may lead to unexpected results. # # #each_strongly_connected_component returns +nil+. # # class G # include Gem::TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # graph.each_strongly_connected_component {|scc| p scc } # #=> [4] # # [2] # # [3] # # [1] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # graph.each_strongly_connected_component {|scc| p scc } # #=> [4] # # [2, 3] # # [1] # def each_strongly_connected_component(&block) # :yields: nodes each_node = method(:tsort_each_node) each_child = method(:tsort_each_child) Gem::TSort.each_strongly_connected_component(each_node, each_child, &block) end # The iterator version of the Gem::TSort.strongly_connected_components method. # # The graph is represented by _each_node_ and _each_child_. # _each_node_ should have +call+ method which yields for each node in the graph. # _each_child_ should have +call+ method which takes a node argument and yields for each child node. # # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } # #=> [4] # # [2] # # [3] # # [1] # # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_node = lambda {|&b| g.each_key(&b) } # each_child = lambda {|n, &b| g[n].each(&b) } # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } # #=> [4] # # [2, 3] # # [1] # def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes return to_enum(__method__, each_node, each_child) unless block_given? id_map = {} stack = [] each_node.call {|node| unless id_map.include? node Gem::TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c| yield c } end } nil end # Iterates over strongly connected component in the subgraph reachable from # _node_. # # Return value is unspecified. # # #each_strongly_connected_component_from doesn't call #tsort_each_node. # # class G # include Gem::TSort # def initialize(g) # @g = g # end # def tsort_each_child(n, &b) @g[n].each(&b) end # def tsort_each_node(&b) @g.each_key(&b) end # end # # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # graph.each_strongly_connected_component_from(2) {|scc| p scc } # #=> [4] # # [2] # # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) # graph.each_strongly_connected_component_from(2) {|scc| p scc } # #=> [4] # # [2, 3] # def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block) end # Iterates over strongly connected components in a graph. # The graph is represented by _node_ and _each_child_. # # _node_ is the first node. # _each_child_ should have +call+ method which takes a node argument # and yields for each child node. # # Return value is unspecified. # # #Gem::TSort.each_strongly_connected_component_from is a class method and # it doesn't need a class to represent a graph which includes Gem::TSort. # # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # each_child = lambda {|n, &b| graph[n].each(&b) } # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc| # p scc # } # #=> [4] # # [2, 3] # # [1] # def TSort.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes return to_enum(__method__, node, each_child, id_map, stack) unless block_given? minimum_id = node_id = id_map[node] = id_map.size stack_length = stack.length stack << node each_child.call(node) {|child| if id_map.include? child child_id = id_map[child] minimum_id = child_id if child_id && child_id < minimum_id else sub_minimum_id = Gem::TSort.each_strongly_connected_component_from(child, each_child, id_map, stack) {|c| yield c } minimum_id = sub_minimum_id if sub_minimum_id < minimum_id end } if node_id == minimum_id component = stack.slice!(stack_length .. -1) component.each {|n| id_map[n] = nil} yield component end minimum_id end # Should be implemented by a extended class. # # #tsort_each_node is used to iterate for all nodes over a graph. # def tsort_each_node # :yields: node raise NotImplementedError.new end # Should be implemented by a extended class. # # #tsort_each_child is used to iterate for child nodes of _node_. # def tsort_each_child(node) # :yields: child raise NotImplementedError.new end end end rubygems/rubygems/dependency_installer.rb 0000644 00000024342 15102661023 0014757 0 ustar 00 # frozen_string_literal: true require_relative '../rubygems' require_relative 'dependency_list' require_relative 'package' require_relative 'installer' require_relative 'spec_fetcher' require_relative 'user_interaction' require_relative 'available_set' require_relative 'deprecate' ## # Installs a gem along with all its dependencies from local and remote gems. class Gem::DependencyInstaller include Gem::UserInteraction extend Gem::Deprecate DEFAULT_OPTIONS = { # :nodoc: :env_shebang => false, :document => %w[ri], :domain => :both, # HACK dup :force => false, :format_executable => false, # HACK dup :ignore_dependencies => false, :prerelease => false, :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? :wrappers => true, :build_args => nil, :build_docs_in_background => false, :install_as_default => false, }.freeze ## # Documentation types. For use by the Gem.done_installing hook attr_reader :document ## # Errors from SpecFetcher while searching for remote specifications attr_reader :errors ## # List of gems installed by #install in alphabetic order attr_reader :installed_gems ## # Creates a new installer instance. # # Options are: # :cache_dir:: Alternate repository path to store .gem files in. # :domain:: :local, :remote, or :both. :local only searches gems in the # current directory. :remote searches only gems in Gem::sources. # :both searches both. # :env_shebang:: See Gem::Installer::new. # :force:: See Gem::Installer#install. # :format_executable:: See Gem::Installer#initialize. # :ignore_dependencies:: Don't install any dependencies. # :install_dir:: See Gem::Installer#install. # :prerelease:: Allow prerelease versions. See #install. # :security_policy:: See Gem::Installer::new and Gem::Security. # :user_install:: See Gem::Installer.new # :wrappers:: See Gem::Installer::new # :build_args:: See Gem::Installer::new def initialize(options = {}) @only_install_dir = !!options[:install_dir] @install_dir = options[:install_dir] || Gem.dir @build_root = options[:build_root] options = DEFAULT_OPTIONS.merge options @bin_dir = options[:bin_dir] @dev_shallow = options[:dev_shallow] @development = options[:development] @document = options[:document] @domain = options[:domain] @env_shebang = options[:env_shebang] @force = options[:force] @format_executable = options[:format_executable] @ignore_dependencies = options[:ignore_dependencies] @prerelease = options[:prerelease] @security_policy = options[:security_policy] @user_install = options[:user_install] @wrappers = options[:wrappers] @build_args = options[:build_args] @build_docs_in_background = options[:build_docs_in_background] @install_as_default = options[:install_as_default] @dir_mode = options[:dir_mode] @data_mode = options[:data_mode] @prog_mode = options[:prog_mode] # Indicates that we should not try to update any deps unless # we absolutely must. @minimal_deps = options[:minimal_deps] @available = nil @installed_gems = [] @toplevel_specs = nil @cache_dir = options[:cache_dir] || @install_dir @errors = [] end ## # Indicated, based on the requested domain, if local # gems should be considered. def consider_local? @domain == :both or @domain == :local end ## # Indicated, based on the requested domain, if remote # gems should be considered. def consider_remote? @domain == :both or @domain == :remote end ## # Returns a list of pairs of gemspecs and source_uris that match # Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources) # sources. Gems are sorted with newer gems preferred over older gems, and # local gems preferred over remote gems. def find_gems_with_sources(dep, best_only=false) # :nodoc: set = Gem::AvailableSet.new if consider_local? sl = Gem::Source::Local.new if spec = sl.find_gem(dep.name) if dep.matches_spec? spec set.add spec, sl end end end if consider_remote? begin # This is pulled from #spec_for_dependency to allow # us to filter tuples before fetching specs. tuples, errors = Gem::SpecFetcher.fetcher.search_for_dependency dep if best_only && !tuples.empty? tuples.sort! do |a,b| if b[0].version == a[0].version if b[0].platform != Gem::Platform::RUBY 1 else -1 end else b[0].version <=> a[0].version end end tuples = [tuples.first] end specs = [] tuples.each do |tup, source| begin spec = source.fetch_spec(tup) rescue Gem::RemoteFetcher::FetchError => e errors << Gem::SourceFetchProblem.new(source, e) else specs << [spec, source] end end if @errors @errors += errors else @errors = errors end set << specs rescue Gem::RemoteFetcher::FetchError => e # FIX if there is a problem talking to the network, we either need to always tell # the user (no really_verbose) or fail hard, not silently tell them that we just # couldn't find their requested gem. verbose do "Error fetching remote data:\t\t#{e.message}\n" \ "Falling back to local-only install" end @domain = :local end end set end rubygems_deprecate :find_gems_with_sources def in_background(what) # :nodoc: fork_happened = false if @build_docs_in_background and Process.respond_to?(:fork) begin Process.fork do yield end fork_happened = true say "#{what} in a background process." rescue NotImplementedError end end yield unless fork_happened end ## # Installs the gem +dep_or_name+ and all its dependencies. Returns an Array # of installed gem specifications. # # If the +:prerelease+ option is set and there is a prerelease for # +dep_or_name+ the prerelease version will be installed. # # Unless explicitly specified as a prerelease dependency, prerelease gems # that +dep_or_name+ depend on will not be installed. # # If c-1.a depends on b-1 and a-1.a and there is a gem b-1.a available then # c-1.a, b-1 and a-1.a will be installed. b-1.a will need to be installed # separately. def install(dep_or_name, version = Gem::Requirement.default) request_set = resolve_dependencies dep_or_name, version @installed_gems = [] options = { :bin_dir => @bin_dir, :build_args => @build_args, :document => @document, :env_shebang => @env_shebang, :force => @force, :format_executable => @format_executable, :ignore_dependencies => @ignore_dependencies, :prerelease => @prerelease, :security_policy => @security_policy, :user_install => @user_install, :wrappers => @wrappers, :build_root => @build_root, :install_as_default => @install_as_default, :dir_mode => @dir_mode, :data_mode => @data_mode, :prog_mode => @prog_mode, } options[:install_dir] = @install_dir if @only_install_dir request_set.install options do |_, installer| @installed_gems << installer.spec if installer end @installed_gems.sort! # Since this is currently only called for docs, we can be lazy and just say # it's documentation. Ideally the hook adder could decide whether to be in # the background or not, and what to call it. in_background "Installing documentation" do Gem.done_installing_hooks.each do |hook| hook.call self, @installed_gems end end unless Gem.done_installing_hooks.empty? @installed_gems end def install_development_deps # :nodoc: if @development and @dev_shallow :shallow elsif @development :all else :none end end def resolve_dependencies(dep_or_name, version) # :nodoc: request_set = Gem::RequestSet.new request_set.development = @development request_set.development_shallow = @dev_shallow request_set.soft_missing = @force request_set.prerelease = @prerelease installer_set = Gem::Resolver::InstallerSet.new @domain installer_set.ignore_installed = (@minimal_deps == false) || @only_install_dir installer_set.force = @force if consider_local? if dep_or_name =~ /\.gem$/ and File.file? dep_or_name src = Gem::Source::SpecificFile.new dep_or_name installer_set.add_local dep_or_name, src.spec, src version = src.spec.version if version == Gem::Requirement.default elsif dep_or_name =~ /\.gem$/ Dir[dep_or_name].each do |name| begin src = Gem::Source::SpecificFile.new name installer_set.add_local dep_or_name, src.spec, src rescue Gem::Package::FormatError end end # else This is a dependency. InstallerSet handles this case end end dependency = if spec = installer_set.local?(dep_or_name) installer_set.remote = nil if spec.dependencies.none? Gem::Dependency.new spec.name, version elsif String === dep_or_name Gem::Dependency.new dep_or_name, version else dep_or_name end dependency.prerelease = @prerelease request_set.import [dependency] installer_set.add_always_install dependency request_set.always_install = installer_set.always_install request_set.remote = installer_set.consider_remote? if @ignore_dependencies installer_set.ignore_dependencies = true request_set.ignore_dependencies = true request_set.soft_missing = true end request_set.resolve installer_set @errors.concat request_set.errors request_set end end rubygems/rubygems/security.rb 0000644 00000054633 15102661023 0012441 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'exceptions' require_relative 'openssl' ## # = Signing gems # # The Gem::Security implements cryptographic signatures for gems. The section # below is a step-by-step guide to using signed gems and generating your own. # # == Walkthrough # # === Building your certificate # # In order to start signing your gems, you'll need to build a private key and # a self-signed certificate. Here's how: # # # build a private key and certificate for yourself: # $ gem cert --build you@example.com # # This could take anywhere from a few seconds to a minute or two, depending on # the speed of your computer (public key algorithms aren't exactly the # speediest crypto algorithms in the world). When it's finished, you'll see # the files "gem-private_key.pem" and "gem-public_cert.pem" in the current # directory. # # First things first: Move both files to ~/.gem if you don't already have a # key and certificate in that directory. Ensure the file permissions make the # key unreadable by others (by default the file is saved securely). # # Keep your private key hidden; if it's compromised, someone can sign packages # as you (note: PKI has ways of mitigating the risk of stolen keys; more on # that later). # # === Signing Gems # # In RubyGems 2 and newer there is no extra work to sign a gem. RubyGems will # automatically find your key and certificate in your home directory and use # them to sign newly packaged gems. # # If your certificate is not self-signed (signed by a third party) RubyGems # will attempt to load the certificate chain from the trusted certificates. # Use <code>gem cert --add signing_cert.pem</code> to add your signers as # trusted certificates. See below for further information on certificate # chains. # # If you build your gem it will automatically be signed. If you peek inside # your gem file, you'll see a couple of new files have been added: # # $ tar tf your-gem-1.0.gem # metadata.gz # metadata.gz.sig # metadata signature # data.tar.gz # data.tar.gz.sig # data signature # checksums.yaml.gz # checksums.yaml.gz.sig # checksums signature # # === Manually signing gems # # If you wish to store your key in a separate secure location you'll need to # set your gems up for signing by hand. To do this, set the # <code>signing_key</code> and <code>cert_chain</code> in the gemspec before # packaging your gem: # # s.signing_key = '/secure/path/to/gem-private_key.pem' # s.cert_chain = %w[/secure/path/to/gem-public_cert.pem] # # When you package your gem with these options set RubyGems will automatically # load your key and certificate from the secure paths. # # === Signed gems and security policies # # Now let's verify the signature. Go ahead and install the gem, but add the # following options: <code>-P HighSecurity</code>, like this: # # # install the gem with using the security policy "HighSecurity" # $ sudo gem install your.gem -P HighSecurity # # The <code>-P</code> option sets your security policy -- we'll talk about # that in just a minute. Eh, what's this? # # $ gem install -P HighSecurity your-gem-1.0.gem # ERROR: While executing gem ... (Gem::Security::Exception) # root cert /CN=you/DC=example is not trusted # # The culprit here is the security policy. RubyGems has several different # security policies. Let's take a short break and go over the security # policies. Here's a list of the available security policies, and a brief # description of each one: # # * NoSecurity - Well, no security at all. Signed packages are treated like # unsigned packages. # * LowSecurity - Pretty much no security. If a package is signed then # RubyGems will make sure the signature matches the signing # certificate, and that the signing certificate hasn't expired, but # that's it. A malicious user could easily circumvent this kind of # security. # * MediumSecurity - Better than LowSecurity and NoSecurity, but still # fallible. Package contents are verified against the signing # certificate, and the signing certificate is checked for validity, # and checked against the rest of the certificate chain (if you don't # know what a certificate chain is, stay tuned, we'll get to that). # The biggest improvement over LowSecurity is that MediumSecurity # won't install packages that are signed by untrusted sources. # Unfortunately, MediumSecurity still isn't totally secure -- a # malicious user can still unpack the gem, strip the signatures, and # distribute the gem unsigned. # * HighSecurity - Here's the bugger that got us into this mess. # The HighSecurity policy is identical to the MediumSecurity policy, # except that it does not allow unsigned gems. A malicious user # doesn't have a whole lot of options here; they can't modify the # package contents without invalidating the signature, and they can't # modify or remove signature or the signing certificate chain, or # RubyGems will simply refuse to install the package. Oh well, maybe # they'll have better luck causing problems for CPAN users instead :). # # The reason RubyGems refused to install your shiny new signed gem was because # it was from an untrusted source. Well, your code is infallible (naturally), # so you need to add yourself as a trusted source: # # # add trusted certificate # gem cert --add ~/.gem/gem-public_cert.pem # # You've now added your public certificate as a trusted source. Now you can # install packages signed by your private key without any hassle. Let's try # the install command above again: # # # install the gem with using the HighSecurity policy (and this time # # without any shenanigans) # $ gem install -P HighSecurity your-gem-1.0.gem # Successfully installed your-gem-1.0 # 1 gem installed # # This time RubyGems will accept your signed package and begin installing. # # While you're waiting for RubyGems to work it's magic, have a look at some of # the other security commands by running <code>gem help cert</code>: # # Options: # -a, --add CERT Add a trusted certificate. # -l, --list [FILTER] List trusted certificates where the # subject contains FILTER # -r, --remove FILTER Remove trusted certificates where the # subject contains FILTER # -b, --build EMAIL_ADDR Build private key and self-signed # certificate for EMAIL_ADDR # -C, --certificate CERT Signing certificate for --sign # -K, --private-key KEY Key for --sign or --build # -A, --key-algorithm ALGORITHM Select key algorithm for --build from RSA, DSA, or EC. Defaults to RSA. # -s, --sign CERT Signs CERT with the key from -K # and the certificate from -C # -d, --days NUMBER_OF_DAYS Days before the certificate expires # -R, --re-sign Re-signs the certificate from -C with the key from -K # # We've already covered the <code>--build</code> option, and the # <code>--add</code>, <code>--list</code>, and <code>--remove</code> commands # seem fairly straightforward; they allow you to add, list, and remove the # certificates in your trusted certificate list. But what's with this # <code>--sign</code> option? # # === Certificate chains # # To answer that question, let's take a look at "certificate chains", a # concept I mentioned earlier. There are a couple of problems with # self-signed certificates: first of all, self-signed certificates don't offer # a whole lot of security. Sure, the certificate says Yukihiro Matsumoto, but # how do I know it was actually generated and signed by matz himself unless he # gave me the certificate in person? # # The second problem is scalability. Sure, if there are 50 gem authors, then # I have 50 trusted certificates, no problem. What if there are 500 gem # authors? 1000? Having to constantly add new trusted certificates is a # pain, and it actually makes the trust system less secure by encouraging # RubyGems users to blindly trust new certificates. # # Here's where certificate chains come in. A certificate chain establishes an # arbitrarily long chain of trust between an issuing certificate and a child # certificate. So instead of trusting certificates on a per-developer basis, # we use the PKI concept of certificate chains to build a logical hierarchy of # trust. Here's a hypothetical example of a trust hierarchy based (roughly) # on geography: # # -------------------------- # | rubygems@rubygems.org | # -------------------------- # | # ----------------------------------- # | | # ---------------------------- ----------------------------- # | seattlerb@seattlerb.org | | dcrubyists@richkilmer.com | # ---------------------------- ----------------------------- # | | | | # --------------- ---------------- ----------- -------------- # | drbrain | | zenspider | | pabs@dc | | tomcope@dc | # --------------- ---------------- ----------- -------------- # # # Now, rather than having 4 trusted certificates (one for drbrain, zenspider, # pabs@dc, and tomecope@dc), a user could actually get by with one # certificate, the "rubygems@rubygems.org" certificate. # # Here's how it works: # # I install "rdoc-3.12.gem", a package signed by "drbrain". I've never heard # of "drbrain", but his certificate has a valid signature from the # "seattle.rb@seattlerb.org" certificate, which in turn has a valid signature # from the "rubygems@rubygems.org" certificate. Voila! At this point, it's # much more reasonable for me to trust a package signed by "drbrain", because # I can establish a chain to "rubygems@rubygems.org", which I do trust. # # === Signing certificates # # The <code>--sign</code> option allows all this to happen. A developer # creates their build certificate with the <code>--build</code> option, then # has their certificate signed by taking it with them to their next regional # Ruby meetup (in our hypothetical example), and it's signed there by the # person holding the regional RubyGems signing certificate, which is signed at # the next RubyConf by the holder of the top-level RubyGems certificate. At # each point the issuer runs the same command: # # # sign a certificate with the specified key and certificate # # (note that this modifies client_cert.pem!) # $ gem cert -K /mnt/floppy/issuer-priv_key.pem -C issuer-pub_cert.pem # --sign client_cert.pem # # Then the holder of issued certificate (in this case, your buddy "drbrain"), # can start using this signed certificate to sign RubyGems. By the way, in # order to let everyone else know about his new fancy signed certificate, # "drbrain" would save his newly signed certificate as # <code>~/.gem/gem-public_cert.pem</code> # # Obviously this RubyGems trust infrastructure doesn't exist yet. Also, in # the "real world", issuers actually generate the child certificate from a # certificate request, rather than sign an existing certificate. And our # hypothetical infrastructure is missing a certificate revocation system. # These are that can be fixed in the future... # # At this point you should know how to do all of these new and interesting # things: # # * build a gem signing key and certificate # * adjust your security policy # * modify your trusted certificate list # * sign a certificate # # == Manually verifying signatures # # In case you don't trust RubyGems you can verify gem signatures manually: # # 1. Fetch and unpack the gem # # gem fetch some_signed_gem # tar -xf some_signed_gem-1.0.gem # # 2. Grab the public key from the gemspec # # gem spec some_signed_gem-1.0.gem cert_chain | \ # ruby -ryaml -e 'puts YAML.load($stdin)' > public_key.crt # # 3. Generate a SHA1 hash of the data.tar.gz # # openssl dgst -sha1 < data.tar.gz > my.hash # # 4. Verify the signature # # openssl rsautl -verify -inkey public_key.crt -certin \ # -in data.tar.gz.sig > verified.hash # # 5. Compare your hash to the verified hash # # diff -s verified.hash my.hash # # 6. Repeat 5 and 6 with metadata.gz # # == OpenSSL Reference # # The .pem files generated by --build and --sign are PEM files. Here's a # couple of useful OpenSSL commands for manipulating them: # # # convert a PEM format X509 certificate into DER format: # # (note: Windows .cer files are X509 certificates in DER format) # $ openssl x509 -in input.pem -outform der -out output.der # # # print out the certificate in a human-readable format: # $ openssl x509 -in input.pem -noout -text # # And you can do the same thing with the private key file as well: # # # convert a PEM format RSA key into DER format: # $ openssl rsa -in input_key.pem -outform der -out output_key.der # # # print out the key in a human readable format: # $ openssl rsa -in input_key.pem -noout -text # # == Bugs/TODO # # * There's no way to define a system-wide trust list. # * custom security policies (from a YAML file, etc) # * Simple method to generate a signed certificate request # * Support for OCSP, SCVP, CRLs, or some other form of cert status check # (list is in order of preference) # * Support for encrypted private keys # * Some sort of semi-formal trust hierarchy (see long-winded explanation # above) # * Path discovery (for gem certificate chains that don't have a self-signed # root) -- by the way, since we don't have this, THE ROOT OF THE CERTIFICATE # CHAIN MUST BE SELF SIGNED if Policy#verify_root is true (and it is for the # MediumSecurity and HighSecurity policies) # * Better explanation of X509 naming (ie, we don't have to use email # addresses) # * Honor AIA field (see note about OCSP above) # * Honor extension restrictions # * Might be better to store the certificate chain as a PKCS#7 or PKCS#12 # file, instead of an array embedded in the metadata. # # == Original author # # Paul Duncan <pabs@pablotron.org> # http://pablotron.org/ module Gem::Security ## # Gem::Security default exception type class Exception < Gem::Exception; end ## # Used internally to select the signing digest from all computed digests DIGEST_NAME = 'SHA256' # :nodoc: ## # Length of keys created by RSA and DSA keys RSA_DSA_KEY_LENGTH = 3072 ## # Default algorithm to use when building a key pair DEFAULT_KEY_ALGORITHM = 'RSA' ## # Named curve used for Elliptic Curve EC_NAME = 'secp384r1' ## # Cipher used to encrypt the key pair used to sign gems. # Must be in the list returned by OpenSSL::Cipher.ciphers KEY_CIPHER = OpenSSL::Cipher.new('AES-256-CBC') if defined?(OpenSSL::Cipher) ## # One day in seconds ONE_DAY = 86400 ## # One year in seconds ONE_YEAR = ONE_DAY * 365 ## # The default set of extensions are: # # * The certificate is not a certificate authority # * The key for the certificate may be used for key and data encipherment # and digital signatures # * The certificate contains a subject key identifier EXTENSIONS = { 'basicConstraints' => 'CA:FALSE', 'keyUsage' => 'keyEncipherment,dataEncipherment,digitalSignature', 'subjectKeyIdentifier' => 'hash', }.freeze def self.alt_name_or_x509_entry(certificate, x509_entry) alt_name = certificate.extensions.find do |extension| extension.oid == "#{x509_entry}AltName" end return alt_name.value if alt_name certificate.send x509_entry end ## # Creates an unsigned certificate for +subject+ and +key+. The lifetime of # the key is from the current time to +age+ which defaults to one year. # # The +extensions+ restrict the key to the indicated uses. def self.create_cert(subject, key, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1) cert = OpenSSL::X509::Certificate.new cert.public_key = get_public_key(key) cert.version = 2 cert.serial = serial cert.not_before = Time.now cert.not_after = Time.now + age cert.subject = subject ef = OpenSSL::X509::ExtensionFactory.new nil, cert cert.extensions = extensions.map do |ext_name, value| ef.create_extension ext_name, value end cert end ## # Gets the right public key from a PKey instance def self.get_public_key(key) # Ruby 3.0 (Ruby/OpenSSL 2.2) or later return OpenSSL::PKey.read(key.public_to_der) if key.respond_to?(:public_to_der) return key.public_key unless key.is_a?(OpenSSL::PKey::EC) ec_key = OpenSSL::PKey::EC.new(key.group.curve_name) ec_key.public_key = key.public_key ec_key end ## # In Ruby 2.3 EC doesn't implement the private_key? but not the private? method if defined?(OpenSSL::PKey::EC) && Gem::Version.new(String.new(RUBY_VERSION)) < Gem::Version.new("2.4.0") OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?) end ## # Creates a self-signed certificate with an issuer and subject from +email+, # a subject alternative name of +email+ and the given +extensions+ for the # +key+. def self.create_cert_email(email, key, age = ONE_YEAR, extensions = EXTENSIONS) subject = email_to_name email extensions = extensions.merge "subjectAltName" => "email:#{email}" create_cert_self_signed subject, key, age, extensions end ## # Creates a self-signed certificate with an issuer and subject of +subject+ # and the given +extensions+ for the +key+. def self.create_cert_self_signed(subject, key, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1) certificate = create_cert subject, key, age, extensions sign certificate, key, certificate, age, extensions, serial end ## # Creates a new digest instance using the specified +algorithm+. The default # is SHA256. if defined?(OpenSSL::Digest) def self.create_digest(algorithm = DIGEST_NAME) OpenSSL::Digest.new(algorithm) end else require 'digest' def self.create_digest(algorithm = DIGEST_NAME) Digest.const_get(algorithm).new end end ## # Creates a new key pair of the specified +algorithm+. RSA, DSA, and EC # are supported. def self.create_key(algorithm) if defined?(OpenSSL::PKey) case algorithm.downcase when 'dsa' OpenSSL::PKey::DSA.new(RSA_DSA_KEY_LENGTH) when 'rsa' OpenSSL::PKey::RSA.new(RSA_DSA_KEY_LENGTH) when 'ec' if RUBY_VERSION >= "2.4.0" OpenSSL::PKey::EC.generate(EC_NAME) else domain_key = OpenSSL::PKey::EC.new(EC_NAME) domain_key.generate_key domain_key end else raise Gem::Security::Exception, "#{algorithm} algorithm not found. RSA, DSA, and EC algorithms are supported." end end end ## # Turns +email_address+ into an OpenSSL::X509::Name def self.email_to_name(email_address) email_address = email_address.gsub(/[^\w@.-]+/i, '_') cn, dcs = email_address.split '@' dcs = dcs.split '.' name = "/CN=#{cn}/#{dcs.map {|dc| "DC=#{dc}" }.join '/'}" OpenSSL::X509::Name.parse name end ## # Signs +expired_certificate+ with +private_key+ if the keys match and the # expired certificate was self-signed. #-- # TODO increment serial def self.re_sign(expired_certificate, private_key, age = ONE_YEAR, extensions = EXTENSIONS) raise Gem::Security::Exception, "incorrect signing key for re-signing " + "#{expired_certificate.subject}" unless expired_certificate.check_private_key(private_key) unless expired_certificate.subject.to_s == expired_certificate.issuer.to_s subject = alt_name_or_x509_entry expired_certificate, :subject issuer = alt_name_or_x509_entry expired_certificate, :issuer raise Gem::Security::Exception, "#{subject} is not self-signed, contact #{issuer} " + "to obtain a valid certificate" end serial = expired_certificate.serial + 1 create_cert_self_signed(expired_certificate.subject, private_key, age, extensions, serial) end ## # Resets the trust directory for verifying gems. def self.reset @trust_dir = nil end ## # Sign the public key from +certificate+ with the +signing_key+ and # +signing_cert+, using the Gem::Security::DIGEST_NAME. Uses the # default certificate validity range and extensions. # # Returns the newly signed certificate. def self.sign(certificate, signing_key, signing_cert, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1) signee_subject = certificate.subject signee_key = certificate.public_key alt_name = certificate.extensions.find do |extension| extension.oid == 'subjectAltName' end extensions = extensions.merge 'subjectAltName' => alt_name.value if alt_name issuer_alt_name = signing_cert.extensions.find do |extension| extension.oid == 'subjectAltName' end extensions = extensions.merge 'issuerAltName' => issuer_alt_name.value if issuer_alt_name signed = create_cert signee_subject, signee_key, age, extensions, serial signed.issuer = signing_cert.subject signed.sign signing_key, Gem::Security::DIGEST_NAME end ## # Returns a Gem::Security::TrustDir which wraps the directory where trusted # certificates live. def self.trust_dir return @trust_dir if @trust_dir dir = File.join Gem.user_home, '.gem', 'trust' @trust_dir ||= Gem::Security::TrustDir.new dir end ## # Enumerates the trusted certificates via Gem::Security::TrustDir. def self.trusted_certificates(&block) trust_dir.each_certificate(&block) end ## # Writes +pemmable+, which must respond to +to_pem+ to +path+ with the given # +permissions+. If passed +cipher+ and +passphrase+ those arguments will be # passed to +to_pem+. def self.write(pemmable, path, permissions = 0600, passphrase = nil, cipher = KEY_CIPHER) path = File.expand_path path File.open path, 'wb', permissions do |io| if passphrase and cipher io.write pemmable.to_pem cipher, passphrase else io.write pemmable.to_pem end end path end reset end if Gem::HAVE_OPENSSL require_relative 'security/policy' require_relative 'security/policies' require_relative 'security/trust_dir' end require_relative 'security/signer' rubygems/rubygems/source.rb 0000644 00000013316 15102661023 0012063 0 ustar 00 # frozen_string_literal: true require_relative "text" ## # A Source knows how to list and fetch gems from a RubyGems marshal index. # # There are other Source subclasses for installed gems, local gems, the # bundler dependency API and so-forth. class Gem::Source include Comparable include Gem::Text FILES = { # :nodoc: :released => 'specs', :latest => 'latest_specs', :prerelease => 'prerelease_specs', }.freeze ## # The URI this source will fetch gems from. attr_reader :uri ## # Creates a new Source which will use the index located at +uri+. def initialize(uri) begin unless uri.kind_of? URI uri = URI.parse(uri.to_s) end rescue URI::InvalidURIError raise if Gem::Source == self.class end @uri = uri end ## # Sources are ordered by installation preference. def <=>(other) case other when Gem::Source::Installed, Gem::Source::Local, Gem::Source::Lock, Gem::Source::SpecificFile, Gem::Source::Git, Gem::Source::Vendor then -1 when Gem::Source then if !@uri return 0 unless other.uri return 1 end return -1 if !other.uri # Returning 1 here ensures that when sorting a list of sources, the # original ordering of sources supplied by the user is preserved. return 1 unless @uri.to_s == other.uri.to_s 0 else nil end end def ==(other) # :nodoc: self.class === other and @uri == other.uri end alias_method :eql?, :== # :nodoc: ## # Returns a Set that can fetch specifications from this source. def dependency_resolver_set # :nodoc: return Gem::Resolver::IndexSet.new self if 'file' == uri.scheme fetch_uri = if uri.host == "rubygems.org" index_uri = uri.dup index_uri.host = "index.rubygems.org" index_uri else uri end bundler_api_uri = enforce_trailing_slash(fetch_uri) begin fetcher = Gem::RemoteFetcher.fetcher response = fetcher.fetch_path bundler_api_uri, nil, true rescue Gem::RemoteFetcher::FetchError Gem::Resolver::IndexSet.new self else Gem::Resolver::APISet.new response.uri + "./info/" end end def hash # :nodoc: @uri.hash end ## # Returns the local directory to write +uri+ to. def cache_dir(uri) # Correct for windows paths escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/') escaped_path.tap(&Gem::UNTAINT) File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path) end ## # Returns true when it is possible and safe to update the cache directory. def update_cache? @update_cache ||= begin File.stat(Gem.user_home).uid == Process.uid rescue Errno::ENOENT false end end ## # Fetches a specification for the given +name_tuple+. def fetch_spec(name_tuple) fetcher = Gem::RemoteFetcher.fetcher spec_file_name = name_tuple.spec_name source_uri = enforce_trailing_slash(uri) + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}" cache_dir = cache_dir source_uri local_spec = File.join cache_dir, spec_file_name if File.exist? local_spec spec = Gem.read_binary local_spec spec = Marshal.load(spec) rescue nil return spec if spec end source_uri.path << '.rz' spec = fetcher.fetch_path source_uri spec = Gem::Util.inflate spec if update_cache? require "fileutils" FileUtils.mkdir_p cache_dir File.open local_spec, 'wb' do |io| io.write spec end end # TODO: Investigate setting Gem::Specification#loaded_from to a URI Marshal.load spec end ## # Loads +type+ kind of specs fetching from +@uri+ if the on-disk cache is # out of date. # # +type+ is one of the following: # # :released => Return the list of all released specs # :latest => Return the list of only the highest version of each gem # :prerelease => Return the list of all prerelease only specs # def load_specs(type) file = FILES[type] fetcher = Gem::RemoteFetcher.fetcher file_name = "#{file}.#{Gem.marshal_version}" spec_path = enforce_trailing_slash(uri) + "#{file_name}.gz" cache_dir = cache_dir spec_path local_file = File.join(cache_dir, file_name) retried = false if update_cache? require "fileutils" FileUtils.mkdir_p cache_dir end spec_dump = fetcher.cache_update_path spec_path, local_file, update_cache? begin Gem::NameTuple.from_list Marshal.load(spec_dump) rescue ArgumentError if update_cache? && !retried FileUtils.rm local_file retried = true retry else raise Gem::Exception.new("Invalid spec cache file in #{local_file}") end end end ## # Downloads +spec+ and writes it to +dir+. See also # Gem::RemoteFetcher#download. def download(spec, dir=Dir.pwd) fetcher = Gem::RemoteFetcher.fetcher fetcher.download spec, uri.to_s, dir end def pretty_print(q) # :nodoc: q.group 2, '[Remote:', ']' do q.breakable q.text @uri.to_s if api = uri q.breakable q.text 'API URI: ' q.text api.to_s end end end def typo_squatting?(host, distance_threshold=4) return if @uri.host.nil? levenshtein_distance(@uri.host, host).between? 1, distance_threshold end private def enforce_trailing_slash(uri) uri.merge(uri.path.gsub(/\/+$/, '') + '/') end end require_relative 'source/git' require_relative 'source/installed' require_relative 'source/specific_file' require_relative 'source/local' require_relative 'source/lock' require_relative 'source/vendor' rubygems/rubygems/psych_tree.rb 0000644 00000001432 15102661023 0012724 0 ustar 00 # frozen_string_literal: true module Gem if defined? ::Psych::Visitors class NoAliasYAMLTree < Psych::Visitors::YAMLTree def self.create new({}) end unless respond_to? :create def visit_String(str) return super unless str == '=' # or whatever you want quote = Psych::Nodes::Scalar::SINGLE_QUOTED @emitter.scalar str, nil, nil, false, true, quote end # Noop this out so there are no anchors def register(target, obj) end # This is ported over from the yaml_tree in 1.9.3 def format_time(time) if time.utc? time.strftime("%Y-%m-%d %H:%M:%S.%9N Z") else time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z") end end private :format_time end end end rubygems/rubygems/dependency_list.rb 0000644 00000013056 15102661023 0013735 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'tsort' require_relative 'deprecate' ## # Gem::DependencyList is used for installing and uninstalling gems in the # correct order to avoid conflicts. #-- # TODO: It appears that all but topo-sort functionality is being duplicated # (or is planned to be duplicated) elsewhere in rubygems. Is the majority of # this class necessary anymore? Especially #ok?, #why_not_ok? class Gem::DependencyList attr_reader :specs include Enumerable include Gem::TSort ## # Allows enabling/disabling use of development dependencies attr_accessor :development ## # Creates a DependencyList from the current specs. def self.from_specs list = new list.add(*Gem::Specification.to_a) list end ## # Creates a new DependencyList. If +development+ is true, development # dependencies will be included. def initialize(development = false) @specs = [] @development = development end ## # Adds +gemspecs+ to the dependency list. def add(*gemspecs) @specs.concat gemspecs end def clear @specs.clear end ## # Return a list of the gem specifications in the dependency list, sorted in # order so that no gemspec in the list depends on a gemspec earlier in the # list. # # This is useful when removing gems from a set of installed gems. By # removing them in the returned order, you don't get into as many dependency # issues. # # If there are circular dependencies (yuck!), then gems will be returned in # order until only the circular dependents and anything they reference are # left. Then arbitrary gemspecs will be returned until the circular # dependency is broken, after which gems will be returned in dependency # order again. def dependency_order sorted = strongly_connected_components.flatten result = [] seen = {} sorted.each do |spec| if index = seen[spec.name] if result[index].version < spec.version result[index] = spec end else seen[spec.name] = result.length result << spec end end result.reverse end ## # Iterator over dependency_order def each(&block) dependency_order.each(&block) end def find_name(full_name) @specs.find {|spec| spec.full_name == full_name } end def inspect # :nodoc: "%s %p>" % [super[0..-2], map {|s| s.full_name }] end ## # Are all the dependencies in the list satisfied? def ok? why_not_ok?(:quick).empty? end def why_not_ok?(quick = false) unsatisfied = Hash.new {|h,k| h[k] = [] } each do |spec| spec.runtime_dependencies.each do |dep| inst = Gem::Specification.any? do |installed_spec| dep.name == installed_spec.name and dep.requirement.satisfied_by? installed_spec.version end unless inst or @specs.find {|s| s.satisfies_requirement? dep } unsatisfied[spec.name] << dep return unsatisfied if quick end end end unsatisfied end ## # It is ok to remove a gemspec from the dependency list? # # If removing the gemspec creates breaks a currently ok dependency, then it # is NOT ok to remove the gemspec. def ok_to_remove?(full_name, check_dev=true) gem_to_remove = find_name full_name # If the state is inconsistent, at least don't crash return true unless gem_to_remove siblings = @specs.find_all do |s| s.name == gem_to_remove.name && s.full_name != gem_to_remove.full_name end deps = [] @specs.each do |spec| check = check_dev ? spec.dependencies : spec.runtime_dependencies check.each do |dep| deps << dep if gem_to_remove.satisfies_requirement?(dep) end end deps.all? do |dep| siblings.any? do |s| s.satisfies_requirement? dep end end end ## # Remove everything in the DependencyList that matches but doesn't # satisfy items in +dependencies+ (a hash of gem names to arrays of # dependencies). def remove_specs_unsatisfied_by(dependencies) specs.reject! do |spec| dep = dependencies[spec.name] dep and not dep.requirement.satisfied_by? spec.version end end ## # Removes the gemspec matching +full_name+ from the dependency list def remove_by_name(full_name) @specs.delete_if {|spec| spec.full_name == full_name } end ## # Return a hash of predecessors. <tt>result[spec]</tt> is an Array of # gemspecs that have a dependency satisfied by the named gemspec. def spec_predecessors result = Hash.new {|h,k| h[k] = [] } specs = @specs.sort.reverse specs.each do |spec| specs.each do |other| next if spec == other other.dependencies.each do |dep| if spec.satisfies_requirement? dep result[spec] << other end end end end result end def tsort_each_node(&block) @specs.each(&block) end def tsort_each_child(node) specs = @specs.sort.reverse dependencies = node.runtime_dependencies dependencies.push(*node.development_dependencies) if @development dependencies.each do |dep| specs.each do |spec| if spec.satisfies_requirement? dep yield spec break end end end end private ## # Count the number of gemspecs in the list +specs+ that are not in # +ignored+. def active_count(specs, ignored) specs.count {|spec| ignored[spec.full_name].nil? } end end rubygems/rubygems/install_update_options.rb 0000644 00000014470 15102661023 0015350 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative '../rubygems' require_relative 'security_option' ## # Mixin methods for install and update options for Gem::Commands module Gem::InstallUpdateOptions include Gem::SecurityOption ## # Add the install/update options to the option parser. def add_install_update_options add_option(:"Install/Update", '-i', '--install-dir DIR', 'Gem repository directory to get installed', 'gems') do |value, options| options[:install_dir] = File.expand_path(value) end add_option(:"Install/Update", '-n', '--bindir DIR', 'Directory where executables will be', 'placed when the gem is installed') do |value, options| options[:bin_dir] = File.expand_path(value) end add_option(:"Install/Update", '--document [TYPES]', Array, 'Generate documentation for installed gems', 'List the documentation types you wish to', 'generate. For example: rdoc,ri') do |value, options| options[:document] = case value when nil then %w[ri] when false then [] else value end end add_option(:"Install/Update", '--build-root DIR', 'Temporary installation root. Useful for building', 'packages. Do not use this when installing remote gems.') do |value, options| options[:build_root] = File.expand_path(value) end add_option(:"Install/Update", '--vendor', 'Install gem into the vendor directory.', 'Only for use by gem repackagers.') do |value, options| unless Gem.vendor_dir raise Gem::OptionParser::InvalidOption.new 'your platform is not supported' end options[:vendor] = true options[:install_dir] = Gem.vendor_dir end add_option(:"Install/Update", '-N', '--no-document', 'Disable documentation generation') do |value, options| options[:document] = [] end add_option(:"Install/Update", '-E', '--[no-]env-shebang', "Rewrite the shebang line on installed", "scripts to use /usr/bin/env") do |value, options| options[:env_shebang] = value end add_option(:"Install/Update", '-f', '--[no-]force', 'Force gem to install, bypassing dependency', 'checks') do |value, options| options[:force] = value end add_option(:"Install/Update", '-w', '--[no-]wrappers', 'Use bin wrappers for executables', 'Not available on dosish platforms') do |value, options| options[:wrappers] = value end add_security_option add_option(:"Install/Update", '--ignore-dependencies', 'Do not install any required dependent gems') do |value, options| options[:ignore_dependencies] = value end add_option(:"Install/Update", '--[no-]format-executable', 'Make installed executable names match Ruby.', 'If Ruby is ruby18, foo_exec will be', 'foo_exec18') do |value, options| options[:format_executable] = value end add_option(:"Install/Update", '--[no-]user-install', 'Install in user\'s home directory instead', 'of GEM_HOME.') do |value, options| options[:user_install] = value end add_option(:"Install/Update", "--development", "Install additional development", "dependencies") do |value, options| options[:development] = true options[:dev_shallow] = true end add_option(:"Install/Update", "--development-all", "Install development dependencies for all", "gems (including dev deps themselves)") do |value, options| options[:development] = true options[:dev_shallow] = false end add_option(:"Install/Update", "--conservative", "Don't attempt to upgrade gems already", "meeting version requirement") do |value, options| options[:conservative] = true options[:minimal_deps] = true end add_option(:"Install/Update", "--[no-]minimal-deps", "Don't upgrade any dependencies that already", "meet version requirements") do |value, options| options[:minimal_deps] = value end add_option(:"Install/Update", "--[no-]post-install-message", "Print post install message") do |value, options| options[:post_install_message] = value end add_option(:"Install/Update", '-g', '--file [FILE]', 'Read from a gem dependencies API file and', 'install the listed gems') do |v,o| v = Gem::GEM_DEP_FILES.find do |file| File.exist? file end unless v unless v message = v ? v : "(tried #{Gem::GEM_DEP_FILES.join ', '})" raise Gem::OptionParser::InvalidArgument, "cannot find gem dependencies file #{message}" end options[:gemdeps] = v end add_option(:"Install/Update", '--without GROUPS', Array, 'Omit the named groups (comma separated)', 'when installing from a gem dependencies', 'file') do |v,o| options[:without_groups].concat v.map {|without| without.intern } end add_option(:"Install/Update", '--default', 'Add the gem\'s full specification to', 'specifications/default and extract only its bin') do |v,o| options[:install_as_default] = v end add_option(:"Install/Update", '--explain', 'Rather than install the gems, indicate which would', 'be installed') do |v,o| options[:explain] = v end add_option(:"Install/Update", '--[no-]lock', 'Create a lock file (when used with -g/--file)') do |v,o| options[:lock] = v end add_option(:"Install/Update", '--[no-]suggestions', 'Suggest alternates when gems are not found') do |v,o| options[:suggest_alternate] = v end end ## # Default options for the gem install command. def install_update_defaults_str '--document=rdoc,ri --wrappers' end end rubygems/rubygems/ext/configure_builder.rb 0000644 00000001066 15102661023 0015051 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder def self.build(extension, dest_path, results, args=[], lib_dir=nil, configure_dir=Dir.pwd) unless File.exist?(File.join(configure_dir, 'Makefile')) cmd = ["sh", "./configure", "--prefix=#{dest_path}", *args] run cmd, results, class_name, configure_dir end make dest_path, results, configure_dir results end end rubygems/rubygems/ext/ext_conf_builder.rb 0000644 00000006475 15102661023 0014706 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd) require 'fileutils' require 'tempfile' tmp_dest = Dir.mktmpdir(".gem.", extension_dir) # Some versions of `mktmpdir` return absolute paths, which will break make # if the paths contain spaces. However, on Ruby 1.9.x on Windows, relative # paths cause all C extension builds to fail. # # As such, we convert to a relative path unless we are using Ruby 1.9.x on # Windows. This means that when using Ruby 1.9.x on Windows, paths with # spaces do not work. # # Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940 tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir) Tempfile.open %w[siteconf .rb], extension_dir do |siteconf| siteconf.puts "require 'rbconfig'" siteconf.puts "dest_path = #{tmp_dest_relative.dump}" %w[sitearchdir sitelibdir].each do |dir| siteconf.puts "RbConfig::MAKEFILE_CONFIG['#{dir}'] = dest_path" siteconf.puts "RbConfig::CONFIG['#{dir}'] = dest_path" end siteconf.close destdir = ENV["DESTDIR"] begin # workaround for https://github.com/oracle/truffleruby/issues/2115 siteconf_path = RUBY_ENGINE == "truffleruby" ? siteconf.path.dup : siteconf.path require "shellwords" cmd = Gem.ruby.shellsplit << "-I" << File.expand_path("../../..", __FILE__) << "-r" << get_relative_path(siteconf_path, extension_dir) << File.basename(extension) cmd.push(*args) begin run(cmd, results, class_name, extension_dir) do |s, r| mkmf_log = File.join(extension_dir, 'mkmf.log') if File.exist? mkmf_log unless s.success? r << "To see why this extension failed to compile, please check" \ " the mkmf.log which can be found here:\n" r << " " + File.join(dest_path, 'mkmf.log') + "\n" end FileUtils.mv mkmf_log, dest_path end end siteconf.unlink end ENV["DESTDIR"] = nil make dest_path, results, extension_dir if tmp_dest_relative full_tmp_dest = File.join(extension_dir, tmp_dest_relative) # TODO remove in RubyGems 3 if Gem.install_extension_in_lib and lib_dir FileUtils.mkdir_p lib_dir entries = Dir.entries(full_tmp_dest) - %w[. ..] entries = entries.map {|entry| File.join full_tmp_dest, entry } FileUtils.cp_r entries, lib_dir, :remove_destination => true end FileUtils::Entry_.new(full_tmp_dest).traverse do |ent| destent = ent.class.new(dest_path, ent.rel) destent.exist? or FileUtils.mv(ent.path, destent.path) end end ensure ENV["DESTDIR"] = destdir siteconf.close! end end results ensure FileUtils.rm_rf tmp_dest if tmp_dest end private def self.get_relative_path(path, base) path[0..base.length - 1] = '.' if path.start_with?(base) path end end rubygems/rubygems/ext/cmake_builder.rb 0000644 00000000730 15102661023 0014145 0 ustar 00 # frozen_string_literal: true class Gem::Ext::CmakeBuilder < Gem::Ext::Builder def self.build(extension, dest_path, results, args=[], lib_dir=nil, cmake_dir=Dir.pwd) unless File.exist?(File.join(cmake_dir, 'Makefile')) require_relative '../command' cmd = ["cmake", ".", "-DCMAKE_INSTALL_PREFIX=#{dest_path}", *Gem::Command.build_args] run cmd, results, class_name, cmake_dir end make dest_path, results, cmake_dir results end end rubygems/rubygems/ext/builder.rb 0000644 00000013207 15102661023 0013010 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative '../user_interaction' class Gem::Ext::Builder include Gem::UserInteraction attr_accessor :build_args # :nodoc: def self.class_name name =~ /Ext::(.*)Builder/ $1.downcase end def self.make(dest_path, results, make_dir = Dir.pwd) unless File.exist? File.join(make_dir, 'Makefile') raise Gem::InstallError, 'Makefile not found' end # try to find make program from Ruby configure arguments first RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/ make_program_name = ENV['MAKE'] || ENV['make'] || $1 unless make_program_name make_program_name = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make' end make_program = Shellwords.split(make_program_name) # The installation of the bundled gems is failed when DESTDIR is empty in mswin platform. destdir = (/\bnmake/i !~ make_program_name || ENV['DESTDIR'] && ENV['DESTDIR'] != "") ? 'DESTDIR=%s' % ENV['DESTDIR'] : '' ['clean', '', 'install'].each do |target| # Pass DESTDIR via command line to override what's in MAKEFLAGS cmd = [ *make_program, destdir, target, ].reject(&:empty?) begin run(cmd, results, "make #{target}".rstrip, make_dir) rescue Gem::InstallError raise unless target == 'clean' # ignore clean failure end end end def self.run(command, results, command_name = nil, dir = Dir.pwd) verbose = Gem.configuration.really_verbose begin rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil if verbose puts("current directory: #{dir}") p(command) end results << "current directory: #{dir}" require "shellwords" results << command.shelljoin require "open3" # Set $SOURCE_DATE_EPOCH for the subprocess. env = {'SOURCE_DATE_EPOCH' => Gem.source_date_epoch_string} output, status = Open3.capture2e(env, *command, :chdir => dir) if verbose puts output else results << output end rescue => error raise Gem::InstallError, "#{command_name || class_name} failed#{error.message}" ensure ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps end unless status.success? results << "Building has failed. See above output for more information on the failure." if verbose end yield(status, results) if block_given? unless status.success? exit_reason = if status.exited? ", exit code #{status.exitstatus}" elsif status.signaled? ", uncaught signal #{status.termsig}" end raise Gem::InstallError, "#{command_name || class_name} failed#{exit_reason}" end end ## # Creates a new extension builder for +spec+. If the +spec+ does not yet # have build arguments, saved, set +build_args+ which is an ARGV-style # array. def initialize(spec, build_args = spec.build_args) @spec = spec @build_args = build_args @gem_dir = spec.full_gem_path @ran_rake = false end ## # Chooses the extension builder class for +extension+ def builder_for(extension) # :nodoc: case extension when /extconf/ then Gem::Ext::ExtConfBuilder when /configure/ then Gem::Ext::ConfigureBuilder when /rakefile/i, /mkrf_conf/i then @ran_rake = true Gem::Ext::RakeBuilder when /CMakeLists.txt/ then Gem::Ext::CmakeBuilder else build_error("No builder for extension '#{extension}'") end end ## # Logs the build +output+, then raises Gem::Ext::BuildError. def build_error(output, backtrace = nil) # :nodoc: gem_make_out = write_gem_make_out output message = <<-EOF ERROR: Failed to build gem native extension. #{output} Gem files will remain installed in #{@gem_dir} for inspection. Results logged to #{gem_make_out} EOF raise Gem::Ext::BuildError, message, backtrace end def build_extension(extension, dest_path) # :nodoc: results = [] builder = builder_for(extension) extension_dir = File.expand_path File.join(@gem_dir, File.dirname(extension)) lib_dir = File.join @spec.full_gem_path, @spec.raw_require_paths.first begin FileUtils.mkdir_p dest_path results = builder.build(extension, dest_path, results, @build_args, lib_dir, extension_dir) verbose { results.join("\n") } write_gem_make_out results.join "\n" rescue => e results << e.message build_error(results.join("\n"), $@) end end ## # Builds extensions. Valid types of extensions are extconf.rb files, # configure scripts and rakefiles or mkrf_conf files. def build_extensions return if @spec.extensions.empty? if @build_args.empty? say "Building native extensions. This could take a while..." else say "Building native extensions with: '#{@build_args.join ' '}'" say "This could take a while..." end dest_path = @spec.extension_dir require "fileutils" FileUtils.rm_f @spec.gem_build_complete_path @spec.extensions.each do |extension| break if @ran_rake build_extension extension, dest_path end FileUtils.touch @spec.gem_build_complete_path end ## # Writes +output+ to gem_make.out in the extension install directory. def write_gem_make_out(output) # :nodoc: destination = File.join @spec.extension_dir, 'gem_make.out' FileUtils.mkdir_p @spec.extension_dir File.open destination, 'wb' do |io| io.puts output end destination end end rubygems/rubygems/ext/rake_builder.rb 0000644 00000001657 15102661023 0014020 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ class Gem::Ext::RakeBuilder < Gem::Ext::Builder def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd) if File.basename(extension) =~ /mkrf_conf/i run([Gem.ruby, File.basename(extension), *args], results, class_name, extension_dir) end rake = ENV['rake'] if rake require "shellwords" rake = rake.shellsplit else begin rake = [Gem.ruby, "-I#{File.expand_path("../..", __dir__)}", "-rrubygems", Gem.bin_path('rake', 'rake')] rescue Gem::Exception rake = [Gem.default_exec_format % 'rake'] end end rake_args = ["RUBYARCHDIR=#{dest_path}", "RUBYLIBDIR=#{dest_path}", *args] run(rake + rake_args, results, class_name, extension_dir) results end end rubygems/rubygems/ext/build_error.rb 0000644 00000000262 15102661023 0013667 0 ustar 00 # frozen_string_literal: true ## # Raised when there is an error while building extensions. require_relative '../exceptions' class Gem::Ext::BuildError < Gem::InstallError end rubygems/rubygems/remote_fetcher.rb 0000644 00000022410 15102661023 0013551 0 ustar 00 # frozen_string_literal: true require_relative '../rubygems' require_relative 'request' require_relative 'request/connection_pools' require_relative 's3_uri_signer' require_relative 'uri_formatter' require_relative 'uri' require_relative 'user_interaction' ## # RemoteFetcher handles the details of fetching gems and gem information from # a remote source. class Gem::RemoteFetcher include Gem::UserInteraction ## # A FetchError exception wraps up the various possible IO and HTTP failures # that could happen while downloading from the internet. class FetchError < Gem::Exception ## # The URI which was being accessed when the exception happened. attr_accessor :uri, :original_uri def initialize(message, uri) uri = Gem::Uri.new(uri) super uri.redact_credentials_from(message) @original_uri = uri.to_s @uri = uri.redacted.to_s end def to_s # :nodoc: "#{super} (#{uri})" end end ## # A FetchError that indicates that the reason for not being # able to fetch data was that the host could not be contacted class UnknownHostError < FetchError end deprecate_constant(:UnknownHostError) @fetcher = nil ## # Cached RemoteFetcher instance. def self.fetcher @fetcher ||= self.new Gem.configuration[:http_proxy] end attr_accessor :headers ## # Initialize a remote fetcher using the source URI and possible proxy # information. # # +proxy+ # * [String]: explicit specification of proxy; overrides any environment # variable setting # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, # HTTP_PROXY_PASS) # * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy # # +headers+: A set of additional HTTP headers to be sent to the server when # fetching the gem. def initialize(proxy=nil, dns=nil, headers={}) require_relative 'core_ext/tcpsocket_init' if Gem.configuration.ipv4_fallback_enabled require 'net/http' require 'stringio' require 'uri' Socket.do_not_reverse_lookup = true @proxy = proxy @pools = {} @pool_lock = Thread::Mutex.new @cert_files = Gem::Request.get_cert_files @headers = headers end ## # Given a name and requirement, downloads this gem into cache and returns the # filename. Returns nil if the gem cannot be located. #-- # Should probably be integrated with #download below, but that will be a # larger, more encompassing effort. -erikh def download_to_cache(dependency) found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency return if found.empty? spec, source = found.max_by {|(s,_)| s.version } download spec, source.uri end ## # Moves the gem +spec+ from +source_uri+ to the cache dir unless it is # already there. If the source_uri is local the gem cache dir copy is # always replaced. def download(spec, source_uri, install_dir = Gem.dir) install_cache_dir = File.join install_dir, "cache" cache_dir = if Dir.pwd == install_dir # see fetch_command install_dir elsif File.writable?(install_cache_dir) || (File.writable?(install_dir) && (not File.exist?(install_cache_dir))) install_cache_dir else File.join Gem.user_dir, "cache" end gem_file_name = File.basename spec.cache_file local_gem_path = File.join cache_dir, gem_file_name require "fileutils" FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir source_uri = Gem::Uri.new(source_uri) scheme = source_uri.scheme # URI.parse gets confused by MS Windows paths with forward slashes. scheme = nil if scheme =~ /^[a-z]$/i # REFACTOR: split this up and dispatch on scheme (eg download_http) # REFACTOR: be sure to clean up fake fetcher when you do this... cleaner case scheme when 'http', 'https', 's3' then unless File.exist? local_gem_path begin verbose "Downloading gem #{gem_file_name}" remote_gem_path = source_uri + "gems/#{gem_file_name}" self.cache_update_path remote_gem_path, local_gem_path rescue FetchError raise if spec.original_platform == spec.platform alternate_name = "#{spec.original_name}.gem" verbose "Failed, downloading gem #{alternate_name}" remote_gem_path = source_uri + "gems/#{alternate_name}" self.cache_update_path remote_gem_path, local_gem_path end end when 'file' then begin path = source_uri.path path = File.dirname(path) if File.extname(path) == '.gem' remote_gem_path = Gem::Util.correct_for_windows_path(File.join(path, 'gems', gem_file_name)) FileUtils.cp(remote_gem_path, local_gem_path) rescue Errno::EACCES local_gem_path = source_uri.to_s end verbose "Using local gem #{local_gem_path}" when nil then # TODO test for local overriding cache source_path = if Gem.win_platform? && source_uri.scheme && !source_uri.path.include?(':') "#{source_uri.scheme}:#{source_uri.path}" else source_uri.path end source_path = Gem::UriFormatter.new(source_path).unescape begin FileUtils.cp source_path, local_gem_path unless File.identical?(source_path, local_gem_path) rescue Errno::EACCES local_gem_path = source_uri.to_s end verbose "Using local gem #{local_gem_path}" else raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}" end local_gem_path end ## # File Fetcher. Dispatched by +fetch_path+. Use it instead. def fetch_file(uri, *_) Gem.read_binary Gem::Util.correct_for_windows_path uri.path end ## # HTTP Fetcher. Dispatched by +fetch_path+. Use it instead. def fetch_http(uri, last_modified = nil, head = false, depth = 0) fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get response = request uri, fetch_type, last_modified do |req| headers.each {|k,v| req.add_field(k,v) } end case response when Net::HTTPOK, Net::HTTPNotModified then response.uri = uri head ? response : response.body when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther, Net::HTTPTemporaryRedirect then raise FetchError.new('too many redirects', uri) if depth > 10 unless location = response['Location'] raise FetchError.new("redirecting but no redirect location was given", uri) end location = Gem::Uri.new location if https?(uri) && !https?(location) raise FetchError.new("redirecting to non-https resource: #{location}", uri) end fetch_http(location, last_modified, head, depth + 1) else raise FetchError.new("bad response #{response.message} #{response.code}", uri) end end alias :fetch_https :fetch_http ## # Downloads +uri+ and returns it as a String. def fetch_path(uri, mtime = nil, head = false) uri = Gem::Uri.new uri unless uri.scheme raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" end data = send "fetch_#{uri.scheme}", uri, mtime, head if data and !head and uri.to_s.end_with?(".gz") begin data = Gem::Util.gunzip data rescue Zlib::GzipFile::Error raise FetchError.new("server did not return a valid file", uri) end end data rescue Timeout::Error, IOError, SocketError, SystemCallError, *(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e raise FetchError.new("#{e.class}: #{e}", uri) end def fetch_s3(uri, mtime = nil, head = false) begin public_uri = s3_uri_signer(uri).sign rescue Gem::S3URISigner::ConfigurationError, Gem::S3URISigner::InstanceProfileError => e raise FetchError.new(e.message, "s3://#{uri.host}") end fetch_https public_uri, mtime, head end # we have our own signing code here to avoid a dependency on the aws-sdk gem def s3_uri_signer(uri) Gem::S3URISigner.new(uri) end ## # Downloads +uri+ to +path+ if necessary. If no path is given, it just # passes the data. def cache_update_path(uri, path = nil, update = true) mtime = path && File.stat(path).mtime rescue nil data = fetch_path(uri, mtime) if data == nil # indicates the server returned 304 Not Modified return Gem.read_binary(path) end if update and path Gem.write_binary(path, data) end data end ## # Performs a Net::HTTP request of type +request_class+ on +uri+ returning # a Net::HTTP response object. request maintains a table of persistent # connections to reduce connect overhead. def request(uri, request_class, last_modified = nil) proxy = proxy_for @proxy, uri pool = pools_for(proxy).pool_for uri request = Gem::Request.new uri, request_class, last_modified, pool request.fetch do |req| yield req if block_given? end end def https?(uri) uri.scheme.downcase == 'https' end def close_all @pools.each_value {|pool| pool.close_all } end private def proxy_for(proxy, uri) Gem::Request.proxy_uri(proxy || Gem::Request.get_proxy_from_env(uri.scheme)) end def pools_for(proxy) @pool_lock.synchronize do @pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files end end end rubygems/rubygems/commands/list_command.rb 0000644 00000002000 15102661023 0015021 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../query_utils' ## # Searches for gems starting with the supplied argument. class Gem::Commands::ListCommand < Gem::Command include Gem::QueryUtils def initialize super 'list', 'Display local gems whose name matches REGEXP', :name => //, :domain => :local, :details => false, :versions => true, :installed => nil, :version => Gem::Requirement.default add_query_options end def arguments # :nodoc: "REGEXP regexp to look for in gem name" end def defaults_str # :nodoc: "--local --no-details" end def description # :nodoc: <<-EOF The list command is used to view the gems you have installed locally. The --details option displays additional details including the summary, the homepage, the author, the locations of different versions of the gem. To search for remote gems use the search command. EOF end def usage # :nodoc: "#{program_name} [REGEXP ...]" end end rubygems/rubygems/commands/generate_index_command.rb 0000644 00000005651 15102661023 0017046 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../indexer' ## # Generates a index files for use as a gem server. # # See `gem help generate_index` class Gem::Commands::GenerateIndexCommand < Gem::Command def initialize super 'generate_index', 'Generates the index files for a gem server directory', :directory => '.', :build_modern => true add_option '-d', '--directory=DIRNAME', 'repository base dir containing gems subdir' do |dir, options| options[:directory] = File.expand_path dir end add_option '--[no-]modern', 'Generate indexes for RubyGems', '(always true)' do |value, options| options[:build_modern] = value end deprecate_option('--modern', version: '4.0', extra_msg: 'Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.') deprecate_option('--no-modern', version: '4.0', extra_msg: 'The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.') add_option '--update', 'Update modern indexes with gems added', 'since the last update' do |value, options| options[:update] = value end end def defaults_str # :nodoc: "--directory . --modern" end def description # :nodoc: <<-EOF The generate_index command creates a set of indexes for serving gems statically. The command expects a 'gems' directory under the path given to the --directory option. The given directory will be the directory you serve as the gem repository. For `gem generate_index --directory /path/to/repo`, expose /path/to/repo via your HTTP server configuration (not /path/to/repo/gems). When done, it will generate a set of files like this: gems/*.gem # .gem files you want to # index specs.<version>.gz # specs index latest_specs.<version>.gz # latest specs index prerelease_specs.<version>.gz # prerelease specs index quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file The .rz extension files are compressed with the inflate algorithm. The Marshal version number comes from ruby's Marshal::MAJOR_VERSION and Marshal::MINOR_VERSION constants. It is used to ensure compatibility. EOF end def execute # This is always true because it's the only way now. options[:build_modern] = true if not File.exist?(options[:directory]) or not File.directory?(options[:directory]) alert_error "unknown directory name #{options[:directory]}." terminate_interaction 1 else indexer = Gem::Indexer.new options.delete(:directory), options if options[:update] indexer.update_index else indexer.generate_index end end end end rubygems/rubygems/commands/sources_command.rb 0000644 00000013353 15102661023 0015546 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../remote_fetcher' require_relative '../spec_fetcher' require_relative '../local_remote_options' class Gem::Commands::SourcesCommand < Gem::Command include Gem::LocalRemoteOptions def initialize require 'fileutils' super 'sources', 'Manage the sources and cache file RubyGems uses to search for gems' add_option '-a', '--add SOURCE_URI', 'Add source' do |value, options| options[:add] = value end add_option '-l', '--list', 'List sources' do |value, options| options[:list] = value end add_option '-r', '--remove SOURCE_URI', 'Remove source' do |value, options| options[:remove] = value end add_option '-c', '--clear-all', 'Remove all sources (clear the cache)' do |value, options| options[:clear_all] = value end add_option '-u', '--update', 'Update source cache' do |value, options| options[:update] = value end add_option '-f', '--[no-]force', "Do not show any confirmation prompts and behave as if 'yes' was always answered" do |value, options| options[:force] = value end add_proxy_option end def add_source(source_uri) # :nodoc: check_rubygems_https source_uri source = Gem::Source.new source_uri check_typo_squatting(source) begin if Gem.sources.include? source say "source #{source_uri} already present in the cache" else source.load_specs :released Gem.sources << source Gem.configuration.write say "#{source_uri} added to sources" end rescue URI::Error, ArgumentError say "#{source_uri} is not a URI" terminate_interaction 1 rescue Gem::RemoteFetcher::FetchError => e say "Error fetching #{source_uri}:\n\t#{e.message}" terminate_interaction 1 end end def check_typo_squatting(source) if source.typo_squatting?("rubygems.org") question = <<-QUESTION.chomp #{source.uri.to_s} is too similar to https://rubygems.org Do you want to add this source? QUESTION terminate_interaction 1 unless options[:force] || ask_yes_no(question) end end def check_rubygems_https(source_uri) # :nodoc: uri = URI source_uri if uri.scheme and uri.scheme.downcase == 'http' and uri.host.downcase == 'rubygems.org' question = <<-QUESTION.chomp https://rubygems.org is recommended for security over #{uri} Do you want to add this insecure source? QUESTION terminate_interaction 1 unless options[:force] || ask_yes_no(question) end end def clear_all # :nodoc: path = Gem.spec_cache_dir FileUtils.rm_rf path unless File.exist? path say "*** Removed specs cache ***" else unless File.writable? path say "*** Unable to remove source cache (write protected) ***" else say "*** Unable to remove source cache ***" end terminate_interaction 1 end end def defaults_str # :nodoc: '--list' end def description # :nodoc: <<-EOF RubyGems fetches gems from the sources you have configured (stored in your ~/.gemrc). The default source is https://rubygems.org, but you may have other sources configured. This guide will help you update your sources or configure yourself to use your own gem server. Without any arguments the sources lists your currently configured sources: $ gem sources *** CURRENT SOURCES *** https://rubygems.org This may list multiple sources or non-rubygems sources. You probably configured them before or have an old `~/.gemrc`. If you have sources you do not recognize you should remove them. RubyGems has been configured to serve gems via the following URLs through its history: * http://gems.rubyforge.org (RubyGems 1.3.6 and earlier) * https://rubygems.org/ (RubyGems 1.3.7 through 1.8.25) * https://rubygems.org (RubyGems 2.0.1 and newer) Since all of these sources point to the same set of gems you only need one of them in your list. https://rubygems.org is recommended as it brings the protections of an SSL connection to gem downloads. To add a source use the --add argument: $ gem sources --add https://rubygems.org https://rubygems.org added to sources RubyGems will check to see if gems can be installed from the source given before it is added. To remove a source use the --remove argument: $ gem sources --remove https://rubygems.org/ https://rubygems.org/ removed from sources EOF end def list # :nodoc: say "*** CURRENT SOURCES ***" say Gem.sources.each do |src| say src end end def list? # :nodoc: !(options[:add] || options[:clear_all] || options[:remove] || options[:update]) end def execute clear_all if options[:clear_all] source_uri = options[:add] add_source source_uri if source_uri source_uri = options[:remove] remove_source source_uri if source_uri update if options[:update] list if list? end def remove_source(source_uri) # :nodoc: unless Gem.sources.include? source_uri say "source #{source_uri} not present in cache" else Gem.sources.delete source_uri Gem.configuration.write say "#{source_uri} removed from sources" end end def update # :nodoc: Gem.sources.each_source do |src| src.load_specs :released src.load_specs :latest end say "source cache successfully updated" end def remove_cache_file(desc, path) # :nodoc: FileUtils.rm_rf path if not File.exist?(path) say "*** Removed #{desc} source cache ***" elsif not File.writable?(path) say "*** Unable to remove #{desc} source cache (write protected) ***" else say "*** Unable to remove #{desc} source cache ***" end end end rubygems/rubygems/commands/unpack_command.rb 0000644 00000010720 15102661023 0015337 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../version_option' require_relative '../security_option' require_relative '../remote_fetcher' require_relative '../package' # forward-declare module Gem::Security # :nodoc: class Policy # :nodoc: end end class Gem::Commands::UnpackCommand < Gem::Command include Gem::VersionOption include Gem::SecurityOption def initialize require 'fileutils' super 'unpack', 'Unpack an installed gem to the current directory', :version => Gem::Requirement.default, :target => Dir.pwd add_option('--target=DIR', 'target directory for unpacking') do |value, options| options[:target] = value end add_option('--spec', 'unpack the gem specification') do |value, options| options[:spec] = true end add_security_option add_version_option end def arguments # :nodoc: "GEMNAME name of gem to unpack" end def defaults_str # :nodoc: "--version '#{Gem::Requirement.default}'" end def description <<-EOF The unpack command allows you to examine the contents of a gem or modify them to help diagnose a bug. You can add the contents of the unpacked gem to the load path using the RUBYLIB environment variable or -I: $ gem unpack my_gem Unpacked gem: '.../my_gem-1.0' [edit my_gem-1.0/lib/my_gem.rb] $ ruby -Imy_gem-1.0/lib -S other_program You can repackage an unpacked gem using the build command. See the build command help for an example. EOF end def usage # :nodoc: "#{program_name} GEMNAME" end #-- # TODO: allow, e.g., 'gem unpack rake-0.3.1'. Find a general solution for # this, so that it works for uninstall as well. (And check other commands # at the same time.) def execute security_policy = options[:security_policy] get_all_gem_names.each do |name| dependency = Gem::Dependency.new name, options[:version] path = get_path dependency unless path alert_error "Gem '#{name}' not installed nor fetchable." next end if @options[:spec] spec, metadata = Gem::Package.raw_spec(path, security_policy) if metadata.nil? alert_error "--spec is unsupported on '#{name}' (old format gem)" next end spec_file = File.basename spec.spec_file FileUtils.mkdir_p @options[:target] if @options[:target] destination = begin if @options[:target] File.join @options[:target], spec_file else spec_file end end File.open destination, 'w' do |io| io.write metadata end else basename = File.basename path, '.gem' target_dir = File.expand_path basename, options[:target] package = Gem::Package.new path, security_policy package.extract_files target_dir say "Unpacked gem: '#{target_dir}'" end end end ## # # Find cached filename in Gem.path. Returns nil if the file cannot be found. # #-- # TODO: see comments in get_path() about general service. def find_in_cache(filename) Gem.path.each do |path| this_path = File.join(path, "cache", filename) return this_path if File.exist? this_path end return nil end ## # Return the full path to the cached gem file matching the given # name and version requirement. Returns 'nil' if no match. # # Example: # # get_path 'rake', '> 0.4' # "/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem" # get_path 'rake', '< 0.1' # nil # get_path 'rak' # nil (exact name required) #-- # TODO: This should be refactored so that it's a general service. I don't # think any of our existing classes are the right place though. Just maybe # 'Cache'? # # TODO: It just uses Gem.dir for now. What's an easy way to get the list of # source directories? def get_path(dependency) return dependency.name if dependency.name =~ /\.gem$/i specs = dependency.matching_specs selected = specs.max_by {|s| s.version } return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless selected return unless dependency.name =~ /^#{selected.name}$/i # We expect to find (basename).gem in the 'cache' directory. Furthermore, # the name match must be exact (ignoring case). path = find_in_cache File.basename selected.cache_file return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless path path end end rubygems/rubygems/commands/cleanup_command.rb 0000644 00000011312 15102661023 0015503 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../dependency_list' require_relative '../uninstaller' class Gem::Commands::CleanupCommand < Gem::Command def initialize super 'cleanup', 'Clean up old versions of installed gems', :force => false, :install_dir => Gem.dir, :check_dev => true add_option('-n', '-d', '--dry-run', 'Do not uninstall gems') do |value, options| options[:dryrun] = true end add_option(:Deprecated, '--dryrun', 'Do not uninstall gems') do |value, options| options[:dryrun] = true end deprecate_option('--dryrun', extra_msg: 'Use --dry-run instead') add_option('-D', '--[no-]check-development', 'Check development dependencies while uninstalling', '(default: true)') do |value, options| options[:check_dev] = value end add_option('--[no-]user-install', 'Cleanup in user\'s home directory instead', 'of GEM_HOME.') do |value, options| options[:user_install] = value end @candidate_gems = nil @default_gems = [] @full = nil @gems_to_cleanup = nil @original_home = nil @original_path = nil @primary_gems = nil end def arguments # :nodoc: "GEMNAME name of gem to cleanup" end def defaults_str # :nodoc: "--no-dry-run" end def description # :nodoc: <<-EOF The cleanup command removes old versions of gems from GEM_HOME that are not required to meet a dependency. If a gem is installed elsewhere in GEM_PATH the cleanup command won't delete it. If no gems are named all gems in GEM_HOME are cleaned. EOF end def usage # :nodoc: "#{program_name} [GEMNAME ...]" end def execute say "Cleaning up installed gems..." if options[:args].empty? done = false last_set = nil until done do clean_gems this_set = @gems_to_cleanup.map {|spec| spec.full_name }.sort done = this_set.empty? || last_set == this_set last_set = this_set end else clean_gems end say "Clean up complete" verbose do skipped = @default_gems.map {|spec| spec.full_name } "Skipped default gems: #{skipped.join ', '}" end end def clean_gems @original_home = Gem.dir @original_path = Gem.path get_primary_gems get_candidate_gems get_gems_to_cleanup @full = Gem::DependencyList.from_specs deplist = Gem::DependencyList.new @gems_to_cleanup.each {|spec| deplist.add spec } deps = deplist.strongly_connected_components.flatten deps.reverse_each do |spec| uninstall_dep spec end Gem::Specification.reset end def get_candidate_gems @candidate_gems = unless options[:args].empty? options[:args].map do |gem_name| Gem::Specification.find_all_by_name gem_name end.flatten else Gem::Specification.to_a end end def get_gems_to_cleanup gems_to_cleanup = @candidate_gems.select do |spec| @primary_gems[spec.name].version != spec.version end default_gems, gems_to_cleanup = gems_to_cleanup.partition do |spec| spec.default_gem? end uninstall_from = options[:user_install] ? Gem.user_dir : @original_home gems_to_cleanup = gems_to_cleanup.select do |spec| spec.base_dir == uninstall_from end @default_gems += default_gems @default_gems.uniq! @gems_to_cleanup = gems_to_cleanup.uniq end def get_primary_gems @primary_gems = {} Gem::Specification.each do |spec| if @primary_gems[spec.name].nil? or @primary_gems[spec.name].version < spec.version @primary_gems[spec.name] = spec end end end def uninstall_dep(spec) return unless @full.ok_to_remove?(spec.full_name, options[:check_dev]) if options[:dryrun] say "Dry Run Mode: Would uninstall #{spec.full_name}" return end say "Attempting to uninstall #{spec.full_name}" uninstall_options = { :executables => false, :version => "= #{spec.version}", } uninstall_options[:user_install] = Gem.user_dir == spec.base_dir uninstaller = Gem::Uninstaller.new spec.name, uninstall_options begin uninstaller.uninstall rescue Gem::DependencyRemovalException, Gem::InstallError, Gem::GemNotInHomeException, Gem::FilePermissionError => e say "Unable to uninstall #{spec.full_name}:" say "\t#{e.class}: #{e.message}" end ensure # Restore path Gem::Uninstaller may have changed Gem.use_paths @original_home, *@original_path end end rubygems/rubygems/commands/cert_command.rb 0000644 00000022200 15102661023 0015007 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../security' class Gem::Commands::CertCommand < Gem::Command def initialize super 'cert', 'Manage RubyGems certificates and signing settings', :add => [], :remove => [], :list => [], :build => [], :sign => [] add_option('-a', '--add CERT', 'Add a trusted certificate.') do |cert_file, options| options[:add] << open_cert(cert_file) end add_option('-l', '--list [FILTER]', 'List trusted certificates where the', 'subject contains FILTER') do |filter, options| filter ||= '' options[:list] << filter end add_option('-r', '--remove FILTER', 'Remove trusted certificates where the', 'subject contains FILTER') do |filter, options| options[:remove] << filter end add_option('-b', '--build EMAIL_ADDR', 'Build private key and self-signed', 'certificate for EMAIL_ADDR') do |email_address, options| options[:build] << email_address end add_option('-C', '--certificate CERT', 'Signing certificate for --sign') do |cert_file, options| options[:issuer_cert] = open_cert(cert_file) options[:issuer_cert_file] = cert_file end add_option('-K', '--private-key KEY', 'Key for --sign or --build') do |key_file, options| options[:key] = open_private_key(key_file) end add_option('-A', '--key-algorithm ALGORITHM', 'Select which key algorithm to use for --build') do |algorithm, options| options[:key_algorithm] = algorithm end add_option('-s', '--sign CERT', 'Signs CERT with the key from -K', 'and the certificate from -C') do |cert_file, options| raise Gem::OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless File.file? cert_file options[:sign] << cert_file end add_option('-d', '--days NUMBER_OF_DAYS', 'Days before the certificate expires') do |days, options| options[:expiration_length_days] = days.to_i end add_option('-R', '--re-sign', 'Re-signs the certificate from -C with the key from -K') do |resign, options| options[:resign] = resign end end def add_certificate(certificate) # :nodoc: Gem::Security.trust_dir.trust_cert certificate say "Added '#{certificate.subject}'" end def check_openssl return if Gem::HAVE_OPENSSL alert_error "OpenSSL library is required for the cert command" terminate_interaction 1 end def open_cert(certificate_file) check_openssl OpenSSL::X509::Certificate.new File.read certificate_file rescue Errno::ENOENT raise Gem::OptionParser::InvalidArgument, "#{certificate_file}: does not exist" rescue OpenSSL::X509::CertificateError raise Gem::OptionParser::InvalidArgument, "#{certificate_file}: invalid X509 certificate" end def open_private_key(key_file) check_openssl passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE'] key = OpenSSL::PKey.read File.read(key_file), passphrase raise Gem::OptionParser::InvalidArgument, "#{key_file}: private key not found" unless key.private? key rescue Errno::ENOENT raise Gem::OptionParser::InvalidArgument, "#{key_file}: does not exist" rescue OpenSSL::PKey::PKeyError, ArgumentError raise Gem::OptionParser::InvalidArgument, "#{key_file}: invalid RSA, DSA, or EC key" end def execute check_openssl options[:add].each do |certificate| add_certificate certificate end options[:remove].each do |filter| remove_certificates_matching filter end options[:list].each do |filter| list_certificates_matching filter end options[:build].each do |email| build email end if options[:resign] re_sign_cert( options[:issuer_cert], options[:issuer_cert_file], options[:key] ) end sign_certificates unless options[:sign].empty? end def build(email) if !valid_email?(email) raise Gem::CommandLineError, "Invalid email address #{email}" end key, key_path = build_key cert_path = build_cert email, key say "Certificate: #{cert_path}" if key_path say "Private Key: #{key_path}" say "Don't forget to move the key file to somewhere private!" end end def build_cert(email, key) # :nodoc: expiration_length_days = options[:expiration_length_days] || Gem.configuration.cert_expiration_length_days cert = Gem::Security.create_cert_email( email, key, (Gem::Security::ONE_DAY * expiration_length_days) ) Gem::Security.write cert, "gem-public_cert.pem" end def build_key # :nodoc: return options[:key] if options[:key] passphrase = ask_for_password 'Passphrase for your Private Key:' say "\n" passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:' say "\n" raise Gem::CommandLineError, "Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation algorithm = options[:key_algorithm] || Gem::Security::DEFAULT_KEY_ALGORITHM key = Gem::Security.create_key(algorithm) key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase return key, key_path end def certificates_matching(filter) return enum_for __method__, filter unless block_given? Gem::Security.trusted_certificates.select do |certificate, _| subject = certificate.subject.to_s subject.downcase.index filter end.sort_by do |certificate, _| certificate.subject.to_a.map {|name, data,| [name, data] } end.each do |certificate, path| yield certificate, path end end def description # :nodoc: <<-EOF The cert command manages signing keys and certificates for creating signed gems. Your signing certificate and private key are typically stored in ~/.gem/gem-public_cert.pem and ~/.gem/gem-private_key.pem respectively. To build a certificate for signing gems: gem cert --build you@example If you already have an RSA key, or are creating a new certificate for an existing key: gem cert --build you@example --private-key /path/to/key.pem If you wish to trust a certificate you can add it to the trust list with: gem cert --add /path/to/cert.pem You can list trusted certificates with: gem cert --list or: gem cert --list cert_subject_substring If you wish to remove a previously trusted certificate: gem cert --remove cert_subject_substring To sign another gem author's certificate: gem cert --sign /path/to/other_cert.pem For further reading on signing gems see `ri Gem::Security`. EOF end def list_certificates_matching(filter) # :nodoc: certificates_matching filter do |certificate, _| # this could probably be formatted more gracefully say certificate.subject.to_s end end def load_default_cert cert_file = File.join Gem.default_cert_path cert = File.read cert_file options[:issuer_cert] = OpenSSL::X509::Certificate.new cert rescue Errno::ENOENT alert_error \ "--certificate not specified and ~/.gem/gem-public_cert.pem does not exist" terminate_interaction 1 rescue OpenSSL::X509::CertificateError alert_error \ "--certificate not specified and ~/.gem/gem-public_cert.pem is not valid" terminate_interaction 1 end def load_default_key key_file = File.join Gem.default_key_path key = File.read key_file passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE'] options[:key] = OpenSSL::PKey.read key, passphrase rescue Errno::ENOENT alert_error \ "--private-key not specified and ~/.gem/gem-private_key.pem does not exist" terminate_interaction 1 rescue OpenSSL::PKey::PKeyError alert_error \ "--private-key not specified and ~/.gem/gem-private_key.pem is not valid" terminate_interaction 1 end def load_defaults # :nodoc: load_default_cert unless options[:issuer_cert] load_default_key unless options[:key] end def remove_certificates_matching(filter) # :nodoc: certificates_matching filter do |certificate, path| FileUtils.rm path say "Removed '#{certificate.subject}'" end end def sign(cert_file) cert = File.read cert_file cert = OpenSSL::X509::Certificate.new cert permissions = File.stat(cert_file).mode & 0777 issuer_cert = options[:issuer_cert] issuer_key = options[:key] cert = Gem::Security.sign cert, issuer_key, issuer_cert Gem::Security.write cert, cert_file, permissions end def sign_certificates # :nodoc: load_defaults unless options[:sign].empty? options[:sign].each do |cert_file| sign cert_file end end def re_sign_cert(cert, cert_path, private_key) Gem::Security::Signer.re_sign_cert(cert, cert_path, private_key) do |expired_cert_path, new_expired_cert_path| alert("Your certificate #{expired_cert_path} has been re-signed") alert("Your expired certificate will be located at: #{new_expired_cert_path}") end end private def valid_email?(email) # It's simple, but is all we need email =~ /\A.+@.+\z/ end end rubygems/rubygems/commands/uninstall_command.rb 0000644 00000013252 15102661023 0016072 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../version_option' require_relative '../uninstaller' require 'fileutils' ## # Gem uninstaller command line tool # # See `gem help uninstall` class Gem::Commands::UninstallCommand < Gem::Command include Gem::VersionOption def initialize super 'uninstall', 'Uninstall gems from the local repository', :version => Gem::Requirement.default, :user_install => true, :check_dev => false, :vendor => false add_option('-a', '--[no-]all', 'Uninstall all matching versions' ) do |value, options| options[:all] = value end add_option('-I', '--[no-]ignore-dependencies', 'Ignore dependency requirements while', 'uninstalling') do |value, options| options[:ignore] = value end add_option('-D', '--[no-]check-development', 'Check development dependencies while uninstalling', '(default: false)') do |value, options| options[:check_dev] = value end add_option('-x', '--[no-]executables', 'Uninstall applicable executables without', 'confirmation') do |value, options| options[:executables] = value end add_option('-i', '--install-dir DIR', 'Directory to uninstall gem from') do |value, options| options[:install_dir] = File.expand_path(value) end add_option('-n', '--bindir DIR', 'Directory to remove executables from') do |value, options| options[:bin_dir] = File.expand_path(value) end add_option('--[no-]user-install', 'Uninstall from user\'s home directory', 'in addition to GEM_HOME.') do |value, options| options[:user_install] = value end add_option('--[no-]format-executable', 'Assume executable names match Ruby\'s prefix and suffix.') do |value, options| options[:format_executable] = value end add_option('--[no-]force', 'Uninstall all versions of the named gems', 'ignoring dependencies') do |value, options| options[:force] = value end add_option('--[no-]abort-on-dependent', 'Prevent uninstalling gems that are', 'depended on by other gems.') do |value, options| options[:abort_on_dependent] = value end add_version_option add_platform_option add_option('--vendor', 'Uninstall gem from the vendor directory.', 'Only for use by gem repackagers.') do |value, options| unless Gem.vendor_dir raise Gem::OptionParser::InvalidOption.new 'your platform is not supported' end alert_warning 'Use your OS package manager to uninstall vendor gems' options[:vendor] = true options[:install_dir] = Gem.vendor_dir end end def arguments # :nodoc: "GEMNAME name of gem to uninstall" end def defaults_str # :nodoc: "--version '#{Gem::Requirement.default}' --no-force " + "--user-install" end def description # :nodoc: <<-EOF The uninstall command removes a previously installed gem. RubyGems will ask for confirmation if you are attempting to uninstall a gem that is a dependency of an existing gem. You can use the --ignore-dependencies option to skip this check. EOF end def usage # :nodoc: "#{program_name} GEMNAME [GEMNAME ...]" end def check_version # :nodoc: if options[:version] != Gem::Requirement.default and get_all_gem_names.size > 1 alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \ " version requirements using `gem uninstall 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`" terminate_interaction 1 end end def execute check_version if options[:all] and not options[:args].empty? uninstall_specific elsif options[:all] uninstall_all else uninstall_specific end end def uninstall_all specs = Gem::Specification.reject {|spec| spec.default_gem? } specs.each do |spec| options[:version] = spec.version uninstall_gem spec.name end alert "Uninstalled all gems in #{options[:install_dir] || Gem.dir}" end def uninstall_specific deplist = Gem::DependencyList.new original_gem_version = {} get_all_gem_names_and_versions.each do |name, version| original_gem_version[name] = version || options[:version] gem_specs = Gem::Specification.find_all_by_name(name, original_gem_version[name]) say("Gem '#{name}' is not installed") if gem_specs.empty? gem_specs.each do |spec| deplist.add spec end end deps = deplist.strongly_connected_components.flatten.reverse gems_to_uninstall = {} deps.each do |dep| unless gems_to_uninstall[dep.name] gems_to_uninstall[dep.name] = true unless original_gem_version[dep.name] == Gem::Requirement.default options[:version] = dep.version end uninstall_gem(dep.name) end end end def uninstall_gem(gem_name) uninstall(gem_name) rescue Gem::GemNotInHomeException => e spec = e.spec alert("In order to remove #{spec.name}, please execute:\n" + "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}") rescue Gem::UninstallError => e spec = e.spec alert_error("Error: unable to successfully uninstall '#{spec.name}' which is " + "located at '#{spec.full_gem_path}'. This is most likely because" + "the current user does not have the appropriate permissions") terminate_interaction 1 end def uninstall(gem_name) Gem::Uninstaller.new(gem_name, options).uninstall end end rubygems/rubygems/commands/info_command.rb 0000644 00000001524 15102661023 0015013 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../query_utils' class Gem::Commands::InfoCommand < Gem::Command include Gem::QueryUtils def initialize super "info", "Show information for the given gem", :name => //, :domain => :local, :details => false, :versions => true, :installed => nil, :version => Gem::Requirement.default add_query_options remove_option('-d') defaults[:details] = true defaults[:exact] = true end def description # :nodoc: "Info prints information about the gem such as name,"\ " description, website, license and installed paths" end def usage # :nodoc: "#{program_name} GEMNAME" end def arguments # :nodoc: "GEMNAME name of the gem to print information about" end def defaults_str "--local" end end rubygems/rubygems/commands/fetch_command.rb 0000644 00000003421 15102661023 0015147 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../local_remote_options' require_relative '../version_option' class Gem::Commands::FetchCommand < Gem::Command include Gem::LocalRemoteOptions include Gem::VersionOption def initialize super 'fetch', 'Download a gem and place it in the current directory' add_bulk_threshold_option add_proxy_option add_source_option add_clear_sources_option add_version_option add_platform_option add_prerelease_option end def arguments # :nodoc: 'GEMNAME name of gem to download' end def defaults_str # :nodoc: "--version '#{Gem::Requirement.default}'" end def description # :nodoc: <<-EOF The fetch command fetches gem files that can be stored for later use or unpacked to examine their contents. See the build command help for an example of unpacking a gem, modifying it, then repackaging it. EOF end def usage # :nodoc: "#{program_name} GEMNAME [GEMNAME ...]" end def execute version = options[:version] || Gem::Requirement.default platform = Gem.platforms.last gem_names = get_all_gem_names gem_names.each do |gem_name| dep = Gem::Dependency.new gem_name, version dep.prerelease = options[:prerelease] specs_and_sources, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep if platform filtered = specs_and_sources.select {|s,| s.platform == platform } specs_and_sources = filtered unless filtered.empty? end spec, source = specs_and_sources.max_by {|s,| s } if spec.nil? show_lookup_failure gem_name, version, errors, options[:domain] next end source.download spec say "Downloaded #{spec.full_name}" end end end rubygems/rubygems/commands/setup_command.rb 0000644 00000046517 15102661023 0015233 0 ustar 00 # frozen_string_literal: true require_relative '../command' ## # Installs RubyGems itself. This command is ordinarily only available from a # RubyGems checkout or tarball. class Gem::Commands::SetupCommand < Gem::Command HISTORY_HEADER = /^#\s*[\d.a-zA-Z]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/.freeze VERSION_MATCHER = /^#\s*([\d.a-zA-Z]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/.freeze ENV_PATHS = %w[/usr/bin/env /bin/env].freeze def initialize require 'tmpdir' super 'setup', 'Install RubyGems', :format_executable => false, :document => %w[ri], :force => true, :site_or_vendor => 'sitelibdir', :destdir => '', :prefix => '', :previous_version => '', :regenerate_binstubs => true, :regenerate_plugins => true add_option '--previous-version=VERSION', 'Previous version of RubyGems', 'Used for changelog processing' do |version, options| options[:previous_version] = version end add_option '--prefix=PREFIX', 'Prefix path for installing RubyGems', 'Will not affect gem repository location' do |prefix, options| options[:prefix] = File.expand_path prefix end add_option '--destdir=DESTDIR', 'Root directory to install RubyGems into', 'Mainly used for packaging RubyGems' do |destdir, options| options[:destdir] = File.expand_path destdir end add_option '--[no-]vendor', 'Install into vendorlibdir not sitelibdir' do |vendor, options| options[:site_or_vendor] = vendor ? 'vendorlibdir' : 'sitelibdir' end add_option '--[no-]format-executable', 'Makes `gem` match ruby', 'If Ruby is ruby18, gem will be gem18' do |value, options| options[:format_executable] = value end add_option '--[no-]document [TYPES]', Array, 'Generate documentation for RubyGems', 'List the documentation types you wish to', 'generate. For example: rdoc,ri' do |value, options| options[:document] = case value when nil then %w[rdoc ri] when false then [] else value end end add_option '--[no-]rdoc', 'Generate RDoc documentation for RubyGems' do |value, options| if value options[:document] << 'rdoc' else options[:document].delete 'rdoc' end options[:document].uniq! end add_option '--[no-]ri', 'Generate RI documentation for RubyGems' do |value, options| if value options[:document] << 'ri' else options[:document].delete 'ri' end options[:document].uniq! end add_option '--[no-]regenerate-binstubs', 'Regenerate gem binstubs' do |value, options| options[:regenerate_binstubs] = value end add_option '--[no-]regenerate-plugins', 'Regenerate gem plugins' do |value, options| options[:regenerate_plugins] = value end add_option '-f', '--[no-]force', 'Forcefully overwrite binstubs' do |value, options| options[:force] = value end add_option('-E', '--[no-]env-shebang', 'Rewrite executables with a shebang', 'of /usr/bin/env') do |value, options| options[:env_shebang] = value end @verbose = nil end def check_ruby_version required_version = Gem::Requirement.new '>= 2.3.0' unless required_version.satisfied_by? Gem.ruby_version alert_error "Expected Ruby version #{required_version}, is #{Gem.ruby_version}" terminate_interaction 1 end end def defaults_str # :nodoc: "--format-executable --document ri --regenerate-binstubs" end def description # :nodoc: <<-EOF Installs RubyGems itself. RubyGems installs RDoc for itself in GEM_HOME. By default this is: #{Gem.dir} If you prefer a different directory, set the GEM_HOME environment variable. RubyGems will install the gem command with a name matching ruby's prefix and suffix. If ruby was installed as `ruby18`, gem will be installed as `gem18`. By default, this RubyGems will install gem as: #{Gem.default_exec_format % 'gem'} EOF end module MakeDirs def mkdir_p(path, **opts) super (@mkdirs ||= []) << path end end def execute @verbose = Gem.configuration.really_verbose check_ruby_version require 'fileutils' if Gem.configuration.really_verbose extend FileUtils::Verbose else extend FileUtils end extend MakeDirs lib_dir, bin_dir = make_destination_dirs man_dir = generate_default_man_dir install_lib lib_dir install_executables bin_dir remove_old_bin_files bin_dir remove_old_lib_files lib_dir # Can be removed one we drop support for bundler 2.2.3 (the last version installing man files to man_dir) remove_old_man_files man_dir if man_dir && File.exist?(man_dir) install_default_bundler_gem bin_dir if mode = options[:dir_mode] @mkdirs.uniq! File.chmod(mode, @mkdirs) end say "RubyGems #{Gem::VERSION} installed" regenerate_binstubs(bin_dir) if options[:regenerate_binstubs] regenerate_plugins(bin_dir) if options[:regenerate_plugins] uninstall_old_gemcutter documentation_success = install_rdoc say if @verbose say "-" * 78 say end if options[:previous_version].empty? options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, '0') end options[:previous_version] = Gem::Version.new(options[:previous_version]) show_release_notes say say "-" * 78 say say "RubyGems installed the following executables:" say bin_file_names.map {|name| "\t#{name}\n" } say unless bin_file_names.grep(/#{File::SEPARATOR}gem$/) say "If `gem` was installed by a previous RubyGems installation, you may need" say "to remove it by hand." say end if documentation_success if options[:document].include? 'rdoc' say "Rdoc documentation was installed. You may now invoke:" say " gem server" say "and then peruse beautifully formatted documentation for your gems" say "with your web browser." say "If you do not wish to install this documentation in the future, use the" say "--no-document flag, or set it as the default in your ~/.gemrc file. See" say "'gem help env' for details." say end if options[:document].include? 'ri' say "Ruby Interactive (ri) documentation was installed. ri is kind of like man " say "pages for Ruby libraries. You may access it like this:" say " ri Classname" say " ri Classname.class_method" say " ri Classname#instance_method" say "If you do not wish to install this documentation in the future, use the" say "--no-document flag, or set it as the default in your ~/.gemrc file. See" say "'gem help env' for details." say end end end def install_executables(bin_dir) prog_mode = options[:prog_mode] || 0755 executables = { 'gem' => 'bin' } executables.each do |tool, path| say "Installing #{tool} executable" if @verbose Dir.chdir path do bin_file = "gem" dest_file = target_bin_path(bin_dir, bin_file) bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}" begin bin = File.readlines bin_file bin[0] = shebang File.open bin_tmp_file, 'w' do |fp| fp.puts bin.join end install bin_tmp_file, dest_file, :mode => prog_mode bin_file_names << dest_file ensure rm bin_tmp_file end next unless Gem.win_platform? begin bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat" File.open bin_cmd_file, 'w' do |file| file.puts <<-TEXT @ECHO OFF IF NOT "%~f0" == "~f0" GOTO :WinNT @"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9 GOTO :EOF :WinNT @"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %* TEXT end install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode ensure rm bin_cmd_file end end end end def shebang if options[:env_shebang] ruby_name = RbConfig::CONFIG['ruby_install_name'] @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } "#!#{@env_path} #{ruby_name}\n" else "#!#{Gem.ruby}\n" end end def install_lib(lib_dir) libs = { 'RubyGems' => 'lib' } libs['Bundler'] = 'bundler/lib' libs.each do |tool, path| say "Installing #{tool}" if @verbose lib_files = files_in path Dir.chdir path do install_file_list(lib_files, lib_dir) end end end def install_rdoc gem_doc_dir = File.join Gem.dir, 'doc' rubygems_name = "rubygems-#{Gem::VERSION}" rubygems_doc_dir = File.join gem_doc_dir, rubygems_name begin Gem.ensure_gem_subdirectories Gem.dir rescue SystemCallError # ignore end if File.writable? gem_doc_dir and (not File.exist? rubygems_doc_dir or File.writable? rubygems_doc_dir) say "Removing old RubyGems RDoc and ri" if @verbose Dir[File.join(Gem.dir, 'doc', 'rubygems-[0-9]*')].each do |dir| rm_rf dir end require_relative '../rdoc' fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION def fake_spec.full_gem_path File.expand_path '../../../..', __FILE__ end generate_ri = options[:document].include? 'ri' generate_rdoc = options[:document].include? 'rdoc' rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri rdoc.generate return true elsif @verbose say "Skipping RDoc generation, #{gem_doc_dir} not writable" say "Set the GEM_HOME environment variable if you want RDoc generated" end return false end def install_default_bundler_gem(bin_dir) specs_dir = File.join(default_dir, "specifications", "default") mkdir_p specs_dir, :mode => 0755 bundler_spec = Dir.chdir("bundler") { Gem::Specification.load("bundler.gemspec") } # Remove bundler-*.gemspec in default specification directory. Dir.entries(specs_dir). select {|gs| gs.start_with?("bundler-") }. each {|gs| File.delete(File.join(specs_dir, gs)) } default_spec_path = File.join(specs_dir, "#{bundler_spec.full_name}.gemspec") Gem.write_binary(default_spec_path, bundler_spec.to_ruby) bundler_spec = Gem::Specification.load(default_spec_path) # The base_dir value for a specification is inferred by walking up from the # folder where the spec was `loaded_from`. In the case of default gems, we # walk up two levels, because they live at `specifications/default/`, whereas # in the case of regular gems we walk up just one level because they live at # `specifications/`. However, in this case, the gem we are installing is # misdetected as a regular gem, when it's a default gem in reality. This is # because when there's a `:destdir`, the `loaded_from` path has changed and # doesn't match `Gem.default_specifications_dir` which is the criteria to # tag a gem as a default gem. So, in that case, write the correct # `@base_dir` directly. bundler_spec.instance_variable_set(:@base_dir, File.dirname(File.dirname(specs_dir))) # Remove gemspec that was same version of vendored bundler. normal_gemspec = File.join(default_dir, "specifications", "bundler-#{bundler_spec.version}.gemspec") if File.file? normal_gemspec File.delete normal_gemspec end # Remove gem files that were same version of vendored bundler. if File.directory? bundler_spec.gems_dir Dir.entries(bundler_spec.gems_dir). select {|default_gem| File.basename(default_gem) == "bundler-#{bundler_spec.version}" }. each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) } end bundler_bin_dir = bundler_spec.bin_dir mkdir_p bundler_bin_dir, :mode => 0755 bundler_spec.executables.each do |e| cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e) end require_relative '../installer' Dir.chdir("bundler") do built_gem = Gem::Package.build(bundler_spec) begin Gem::Installer.at( built_gem, env_shebang: options[:env_shebang], format_executable: options[:format_executable], force: options[:force], install_as_default: true, bin_dir: bin_dir, install_dir: default_dir, wrappers: true ).install ensure FileUtils.rm_f built_gem end end bundler_spec.executables.each {|executable| bin_file_names << target_bin_path(bin_dir, executable) } say "Bundler #{bundler_spec.version} installed" end def make_destination_dirs lib_dir, bin_dir = Gem.default_rubygems_dirs unless lib_dir lib_dir, bin_dir = generate_default_dirs end mkdir_p lib_dir, :mode => 0755 mkdir_p bin_dir, :mode => 0755 return lib_dir, bin_dir end def generate_default_man_dir prefix = options[:prefix] if prefix.empty? man_dir = RbConfig::CONFIG['mandir'] return unless man_dir else man_dir = File.join prefix, 'man' end prepend_destdir_if_present(man_dir) end def generate_default_dirs prefix = options[:prefix] site_or_vendor = options[:site_or_vendor] if prefix.empty? lib_dir = RbConfig::CONFIG[site_or_vendor] bin_dir = RbConfig::CONFIG['bindir'] else # Apple installed RubyGems into libdir, and RubyGems <= 1.1.0 gets # confused about installation location, so switch back to # sitelibdir/vendorlibdir. if defined?(APPLE_GEM_HOME) and # just in case Apple and RubyGems don't get this patched up proper. (prefix == RbConfig::CONFIG['libdir'] or # this one is important prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) lib_dir = RbConfig::CONFIG[site_or_vendor] bin_dir = RbConfig::CONFIG['bindir'] else lib_dir = File.join prefix, 'lib' bin_dir = File.join prefix, 'bin' end end [prepend_destdir_if_present(lib_dir), prepend_destdir_if_present(bin_dir)] end def files_in(dir) Dir.chdir dir do Dir.glob(File.join('**', '*'), File::FNM_DOTMATCH). select{|f| !File.directory?(f) } end end def remove_old_bin_files(bin_dir) old_bin_files = { 'gem_mirror' => 'gem mirror', 'gem_server' => 'gem server', 'gemlock' => 'gem lock', 'gemri' => 'ri', 'gemwhich' => 'gem which', 'index_gem_repository.rb' => 'gem generate_index', } old_bin_files.each do |old_bin_file, new_name| old_bin_path = File.join bin_dir, old_bin_file next unless File.exist? old_bin_path deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead." File.open old_bin_path, 'w' do |fp| fp.write <<-EOF #!#{Gem.ruby} abort "#{deprecation_message}" EOF end next unless Gem.win_platform? File.open "#{old_bin_path}.bat", 'w' do |fp| fp.puts %(@ECHO.#{deprecation_message}) end end end def remove_old_lib_files(lib_dir) lib_dirs = { File.join(lib_dir, 'rubygems') => 'lib/rubygems' } lib_dirs[File.join(lib_dir, 'bundler')] = 'bundler/lib/bundler' lib_dirs.each do |old_lib_dir, new_lib_dir| lib_files = files_in(new_lib_dir) old_lib_files = files_in(old_lib_dir) to_remove = old_lib_files - lib_files gauntlet_rubygems = File.join(lib_dir, 'gauntlet_rubygems.rb') to_remove << gauntlet_rubygems if File.exist? gauntlet_rubygems to_remove.delete_if do |file| file.start_with? 'defaults' end remove_file_list(to_remove, old_lib_dir) end end def remove_old_man_files(old_man_dir) old_man1_dir = "#{old_man_dir}/man1" if File.exist?(old_man1_dir) man1_to_remove = Dir.chdir(old_man1_dir) { Dir["bundle*.1{,.txt,.ronn}"] } remove_file_list(man1_to_remove, old_man1_dir) end old_man5_dir = "#{old_man_dir}/man5" if File.exist?(old_man5_dir) man5_to_remove = Dir.chdir(old_man5_dir) { Dir["gemfile.5{,.txt,.ronn}"] } remove_file_list(man5_to_remove, old_man5_dir) end end def show_release_notes release_notes = File.join Dir.pwd, 'CHANGELOG.md' release_notes = if File.exist? release_notes history = File.read release_notes history.force_encoding Encoding::UTF_8 text = history.split(HISTORY_HEADER) text.shift # correct an off-by-one generated by split version_lines = history.scan(HISTORY_HEADER) versions = history.scan(VERSION_MATCHER).flatten.map do |x| Gem::Version.new(x) end history_string = "" until versions.length == 0 or versions.shift <= options[:previous_version] do history_string += version_lines.shift + text.shift end history_string else "Oh-no! Unable to find release notes!" end say release_notes end def uninstall_old_gemcutter require_relative '../uninstaller' ui = Gem::Uninstaller.new('gemcutter', :all => true, :ignore => true, :version => '< 0.4') ui.uninstall rescue Gem::InstallError end def regenerate_binstubs(bindir) require_relative "pristine_command" say "Regenerating binstubs" args = %w[--all --only-executables --silent] args << "--bindir=#{bindir}" if options[:env_shebang] args << "--env-shebang" end command = Gem::Commands::PristineCommand.new command.invoke(*args) end def regenerate_plugins(bindir) require_relative "pristine_command" say "Regenerating plugins" args = %w[--all --only-plugins --silent] args << "--bindir=#{bindir}" args << "--install-dir=#{default_dir}" command = Gem::Commands::PristineCommand.new command.invoke(*args) end private def default_dir prefix = options[:prefix] if prefix.empty? dir = Gem.default_dir else dir = prefix end prepend_destdir_if_present(dir) end def prepend_destdir_if_present(path) destdir = options[:destdir] return path if destdir.empty? File.join(options[:destdir], path.gsub(/^[a-zA-Z]:/, '')) end def install_file_list(files, dest_dir) files.each do |file| install_file file, dest_dir end end def install_file(file, dest_dir) dest_file = File.join dest_dir, file dest_dir = File.dirname dest_file unless File.directory? dest_dir mkdir_p dest_dir, :mode => 0755 end install file, dest_file, :mode => options[:data_mode] || 0644 end def remove_file_list(files, dir) Dir.chdir dir do files.each do |file| FileUtils.rm_f file warn "unable to remove old file #{file} please remove it by hand" if File.exist? file end end end def target_bin_path(bin_dir, bin_file) bin_file_formatted = if options[:format_executable] Gem.default_exec_format % bin_file else bin_file end File.join bin_dir, bin_file_formatted end def bin_file_names @bin_file_names ||= [] end end rubygems/rubygems/commands/owner_command.rb 0000644 00000005712 15102661023 0015215 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../local_remote_options' require_relative '../gemcutter_utilities' require_relative '../text' class Gem::Commands::OwnerCommand < Gem::Command include Gem::Text include Gem::LocalRemoteOptions include Gem::GemcutterUtilities def description # :nodoc: <<-EOF The owner command lets you add and remove owners of a gem on a push server (the default is https://rubygems.org). The owner of a gem has the permission to push new versions, yank existing versions or edit the HTML page of the gem. Be careful of who you give push permission to. EOF end def arguments # :nodoc: "GEM gem to manage owners for" end def usage # :nodoc: "#{program_name} GEM" end def initialize super 'owner', 'Manage gem owners of a gem on the push server' add_proxy_option add_key_option add_otp_option defaults.merge! :add => [], :remove => [] add_option '-a', '--add EMAIL', 'Add an owner' do |value, options| options[:add] << value end add_option '-r', '--remove EMAIL', 'Remove an owner' do |value, options| options[:remove] << value end add_option '-h', '--host HOST', 'Use another gemcutter-compatible host', ' (e.g. https://rubygems.org)' do |value, options| options[:host] = value end end def execute @host = options[:host] sign_in(scope: get_owner_scope) name = get_one_gem_name add_owners name, options[:add] remove_owners name, options[:remove] show_owners name end def show_owners(name) Gem.load_yaml response = rubygems_api_request :get, "api/v1/gems/#{name}/owners.yaml" do |request| request.add_field "Authorization", api_key end with_response response do |resp| owners = Gem::SafeYAML.load clean_text(resp.body) say "Owners for gem: #{name}" owners.each do |owner| say "- #{owner['email'] || owner['handle'] || owner['id']}" end end end def add_owners(name, owners) manage_owners :post, name, owners end def remove_owners(name, owners) manage_owners :delete, name, owners end def manage_owners(method, name, owners) owners.each do |owner| begin response = send_owner_request(method, name, owner) action = method == :delete ? "Removing" : "Adding" with_response response, "#{action} #{owner}" rescue # ignore end end end private def send_owner_request(method, name, owner) rubygems_api_request method, "api/v1/gems/#{name}/owners", scope: get_owner_scope(method: method) do |request| request.set_form_data 'email' => owner request.add_field "Authorization", api_key end end def get_owner_scope(method: nil) if method == :post || options.any? && options[:add].any? :add_owner elsif method == :delete || options.any? && options[:remove].any? :remove_owner end end end rubygems/rubygems/commands/open_command.rb 0000644 00000003654 15102661023 0015027 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../version_option' class Gem::Commands::OpenCommand < Gem::Command include Gem::VersionOption def initialize super 'open', 'Open gem sources in editor' add_option('-e', '--editor COMMAND', String, "Prepends COMMAND to gem path. Could be used to specify editor.") do |command, options| options[:editor] = command || get_env_editor end add_option('-v', '--version VERSION', String, "Opens specific gem version") do |version| options[:version] = version end end def arguments # :nodoc: "GEMNAME name of gem to open in editor" end def defaults_str # :nodoc: "-e #{get_env_editor}" end def description # :nodoc: <<-EOF The open command opens gem in editor and changes current path to gem's source directory. Editor command can be specified with -e option, otherwise rubygems will look for editor in $EDITOR, $VISUAL and $GEM_EDITOR variables. EOF end def usage # :nodoc: "#{program_name} [-e COMMAND] GEMNAME" end def get_env_editor ENV['GEM_EDITOR'] || ENV['VISUAL'] || ENV['EDITOR'] || 'vi' end def execute @version = options[:version] || Gem::Requirement.default @editor = options[:editor] || get_env_editor found = open_gem(get_one_gem_name) terminate_interaction 1 unless found end def open_gem(name) spec = spec_for name return false unless spec if spec.default_gem? say "'#{name}' is a default gem and can't be opened." return false end open_editor(spec.full_gem_path) end def open_editor(path) Dir.chdir(path) do system(*@editor.split(/\s+/) + [path]) end end def spec_for(name) spec = Gem::Specification.find_all_by_name(name, @version).first return spec if spec say "Unable to find gem '#{name}'" end end rubygems/rubygems/commands/signout_command.rb 0000644 00000001601 15102661023 0015544 0 ustar 00 # frozen_string_literal: true require_relative '../command' class Gem::Commands::SignoutCommand < Gem::Command def initialize super 'signout', 'Sign out from all the current sessions.' end def description # :nodoc: 'The `signout` command is used to sign out from all current sessions,'\ ' allowing you to sign in using a different set of credentials.' end def usage # :nodoc: program_name end def execute credentials_path = Gem.configuration.credentials_path if !File.exist?(credentials_path) alert_error 'You are not currently signed in.' elsif !File.writable?(credentials_path) alert_error "File '#{Gem.configuration.credentials_path}' is read-only."\ ' Please make sure it is writable.' else Gem.configuration.unset_api_key! say 'You have successfully signed out from all sessions.' end end end rubygems/rubygems/commands/server_command.rb 0000644 00000004753 15102661023 0015375 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../server' require_relative '../deprecate' class Gem::Commands::ServerCommand < Gem::Command extend Gem::Deprecate rubygems_deprecate_command def initialize super 'server', 'Documentation and gem repository HTTP server', :port => 8808, :gemdir => [], :daemon => false Gem::OptionParser.accept :Port do |port| if port =~ /\A\d+\z/ port = Integer port raise Gem::OptionParser::InvalidArgument, "#{port}: not a port number" if port > 65535 port else begin Socket.getservbyname port rescue SocketError raise Gem::OptionParser::InvalidArgument, "#{port}: no such named service" end end end add_option '-p', '--port=PORT', :Port, 'port to listen on' do |port, options| options[:port] = port end add_option '-d', '--dir=GEMDIR', 'directories from which to serve gems', 'multiple directories may be provided' do |gemdir, options| options[:gemdir] << File.expand_path(gemdir) end add_option '--[no-]daemon', 'run as a daemon' do |daemon, options| options[:daemon] = daemon end add_option '-b', '--bind=HOST,HOST', 'addresses to bind', Array do |address, options| options[:addresses] ||= [] options[:addresses].push(*address) end add_option '-l', '--launch[=COMMAND]', 'launches a browser window', "COMMAND defaults to 'start' on Windows", "and 'open' on all other platforms" do |launch, options| launch ||= Gem.win_platform? ? 'start' : 'open' options[:launch] = launch end end def defaults_str # :nodoc: "--port 8808 --dir #{Gem.dir} --no-daemon" end def description # :nodoc: <<-EOF The server command starts up a web server that hosts the RDoc for your installed gems and can operate as a server for installation of gems on other machines. The cache files for installed gems must exist to use the server as a source for gem installation. To install gems from a running server, use `gem install GEMNAME --source http://gem_server_host:8808` You can set up a shortcut to gem server documentation using the URL: http://localhost:8808/rdoc?q=%s - Firefox http://localhost:8808/rdoc?q=* - LaunchBar EOF end def execute options[:gemdir] = Gem.path if options[:gemdir].empty? Gem::Server.run options end end rubygems/rubygems/commands/outdated_command.rb 0000644 00000001537 15102661023 0015675 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../local_remote_options' require_relative '../spec_fetcher' require_relative '../version_option' class Gem::Commands::OutdatedCommand < Gem::Command include Gem::LocalRemoteOptions include Gem::VersionOption def initialize super 'outdated', 'Display all gems that need updates' add_local_remote_options add_platform_option end def description # :nodoc: <<-EOF The outdated command lists gems you may wish to upgrade to a newer version. You can check for dependency mismatches using the dependency command and update the gems with the update or install commands. EOF end def execute Gem::Specification.outdated_and_latest_version.each do |spec, remote_version| say "#{spec.name} (#{spec.version} < #{remote_version})" end end end rubygems/rubygems/commands/yank_command.rb 0000644 00000004532 15102661023 0015024 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../local_remote_options' require_relative '../version_option' require_relative '../gemcutter_utilities' class Gem::Commands::YankCommand < Gem::Command include Gem::LocalRemoteOptions include Gem::VersionOption include Gem::GemcutterUtilities def description # :nodoc: <<-EOF The yank command permanently removes a gem you pushed to a server. Once you have pushed a gem several downloads will happen automatically via the webhooks. If you accidentally pushed passwords or other sensitive data you will need to change them immediately and yank your gem. EOF end def arguments # :nodoc: "GEM name of gem" end def usage # :nodoc: "#{program_name} -v VERSION [-p PLATFORM] [--key KEY_NAME] [--host HOST] GEM" end def initialize super 'yank', 'Remove a pushed gem from the index' add_version_option("remove") add_platform_option("remove") add_otp_option add_option('--host HOST', 'Yank from another gemcutter-compatible host', ' (e.g. https://rubygems.org)') do |value, options| options[:host] = value end add_key_option @host = nil end def execute @host = options[:host] sign_in @host, scope: get_yank_scope version = get_version_from_requirements(options[:version]) platform = get_platform_from_requirements(options) if version yank_gem(version, platform) else say "A version argument is required: #{usage}" terminate_interaction end end def yank_gem(version, platform) say "Yanking gem from #{self.host}..." args = [:delete, version, platform, "api/v1/gems/yank"] response = yank_api_request(*args) say response.body end private def yank_api_request(method, version, platform, api) name = get_one_gem_name response = rubygems_api_request(method, api, host, scope: get_yank_scope) do |request| request.add_field("Authorization", api_key) data = { 'gem_name' => name, 'version' => version, } data['platform'] = platform if platform request.set_form_data data end response end def get_version_from_requirements(requirements) requirements.requirements.first[1].version rescue nil end def get_yank_scope :yank_rubygem end end rubygems/rubygems/commands/push_command.rb 0000644 00000005201 15102661023 0015033 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../local_remote_options' require_relative '../gemcutter_utilities' require_relative '../package' class Gem::Commands::PushCommand < Gem::Command include Gem::LocalRemoteOptions include Gem::GemcutterUtilities def description # :nodoc: <<-EOF The push command uploads a gem to the push server (the default is https://rubygems.org) and adds it to the index. The gem can be removed from the index and deleted from the server using the yank command. For further discussion see the help for the yank command. The push command will use ~/.gem/credentials to authenticate to a server, but you can use the RubyGems environment variable GEM_HOST_API_KEY to set the api key to authenticate. EOF end def arguments # :nodoc: "GEM built gem to push up" end def usage # :nodoc: "#{program_name} GEM" end def initialize super 'push', 'Push a gem up to the gem server', :host => self.host @user_defined_host = false add_proxy_option add_key_option add_otp_option add_option('--host HOST', 'Push to another gemcutter-compatible host', ' (e.g. https://rubygems.org)') do |value, options| options[:host] = value @user_defined_host = true end @host = nil end def execute gem_name = get_one_gem_name default_gem_server, push_host = get_hosts_for(gem_name) @host = if @user_defined_host options[:host] elsif default_gem_server default_gem_server elsif push_host push_host else options[:host] end sign_in @host, scope: get_push_scope send_gem(gem_name) end def send_gem(name) args = [:post, "api/v1/gems"] _, push_host = get_hosts_for(name) @host ||= push_host # Always include @host, even if it's nil args += [ @host, push_host ] say "Pushing gem to #{@host || Gem.host}..." response = send_push_request(name, args) with_response response end private def send_push_request(name, args) rubygems_api_request(*args, scope: get_push_scope) do |request| request.body = Gem.read_binary name request.add_field "Content-Length", request.body.size request.add_field "Content-Type", "application/octet-stream" request.add_field "Authorization", api_key end end def get_hosts_for(name) gem_metadata = Gem::Package.new(name).spec.metadata [ gem_metadata["default_gem_server"], gem_metadata["allowed_push_host"], ] end def get_push_scope :push_rubygem end end rubygems/rubygems/commands/help_command.rb 0000644 00000024270 15102661023 0015013 0 ustar 00 # frozen_string_literal: true require_relative '../command' class Gem::Commands::HelpCommand < Gem::Command # :stopdoc: EXAMPLES = <<-EOF.freeze Some examples of 'gem' usage. * Install 'rake', either from local directory or remote server: gem install rake * Install 'rake', only from remote server: gem install rake --remote * Install 'rake', but only version 0.3.1, even if dependencies are not met, and into a user-specific directory: gem install rake --version 0.3.1 --force --user-install * List local gems whose name begins with 'D': gem list D * List local and remote gems whose name contains 'log': gem search log --both * List only remote gems whose name contains 'log': gem search log --remote * Uninstall 'rake': gem uninstall rake * Create a gem: See https://guides.rubygems.org/make-your-own-gem/ * See information about RubyGems: gem environment * Update all gems on your system: gem update * Update your local version of RubyGems gem update --system EOF GEM_DEPENDENCIES = <<-EOF.freeze A gem dependencies file allows installation of a consistent set of gems across multiple environments. The RubyGems implementation is designed to be compatible with Bundler's Gemfile format. You can see additional documentation on the format at: http://bundler.io RubyGems automatically looks for these gem dependencies files: * gem.deps.rb * Gemfile * Isolate These files are looked up automatically using `gem install -g`, or you can specify a custom file. When the RUBYGEMS_GEMDEPS environment variable is set to a gem dependencies file the gems from that file will be activated at startup time. Set it to a specific filename or to "-" to have RubyGems automatically discover the gem dependencies file by walking up from the current directory. You can also activate gem dependencies at program startup using Gem.use_gemdeps. NOTE: Enabling automatic discovery on multiuser systems can lead to execution of arbitrary code when used from directories outside your control. Gem Dependencies ================ Use #gem to declare which gems you directly depend upon: gem 'rake' To depend on a specific set of versions: gem 'rake', '~> 10.3', '>= 10.3.2' RubyGems will require the gem name when activating the gem using the RUBYGEMS_GEMDEPS environment variable or Gem::use_gemdeps. Use the require: option to override this behavior if the gem does not have a file of that name or you don't want to require those files: gem 'my_gem', require: 'other_file' To prevent RubyGems from requiring any files use: gem 'my_gem', require: false To load dependencies from a .gemspec file: gemspec RubyGems looks for the first .gemspec file in the current directory. To override this use the name: option: gemspec name: 'specific_gem' To look in a different directory use the path: option: gemspec name: 'specific_gem', path: 'gemspecs' To depend on a gem unpacked into a local directory: gem 'modified_gem', path: 'vendor/modified_gem' To depend on a gem from git: gem 'private_gem', git: 'git@my.company.example:private_gem.git' To depend on a gem from github: gem 'private_gem', github: 'my_company/private_gem' To depend on a gem from a github gist: gem 'bang', gist: '1232884' Git, github and gist support the ref:, branch: and tag: options to specify a commit reference or hash, branch or tag respectively to use for the gem. Setting the submodules: option to true for git, github and gist dependencies causes fetching of submodules when fetching the repository. You can depend on multiple gems from a single repository with the git method: git 'https://github.com/rails/rails.git' do gem 'activesupport' gem 'activerecord' end Gem Sources =========== RubyGems uses the default sources for regular `gem install` for gem dependencies files. Unlike bundler, you do need to specify a source. You can override the sources used for downloading gems with: source 'https://gem_server.example' You may specify multiple sources. Unlike bundler the prepend: option is not supported. Sources are used in-order, to prepend a source place it at the front of the list. Gem Platform ============ You can restrict gem dependencies to specific platforms with the #platform and #platforms methods: platform :ruby_21 do gem 'debugger' end See the bundler Gemfile manual page for a list of platforms supported in a gem dependencies file.: http://bundler.io/v1.6/man/gemfile.5.html Ruby Version and Engine Dependency ================================== You can specify the version, engine and engine version of ruby to use with your gem dependencies file. If you are not running the specified version RubyGems will raise an exception. To depend on a specific version of ruby: ruby '2.1.2' To depend on a specific ruby engine: ruby '1.9.3', engine: 'jruby' To depend on a specific ruby engine version: ruby '1.9.3', engine: 'jruby', engine_version: '1.7.11' Grouping Dependencies ===================== Gem dependencies may be placed in groups that can be excluded from install. Dependencies required for development or testing of your code may be excluded when installed in a production environment. A #gem dependency may be placed in a group using the group: option: gem 'minitest', group: :test To install dependencies from a gemfile without specific groups use the `--without` option for `gem install -g`: $ gem install -g --without test The group: option also accepts multiple groups if the gem fits in multiple categories. Multiple groups may be excluded during install by comma-separating the groups for `--without` or by specifying `--without` multiple times. The #group method can also be used to place gems in groups: group :test do gem 'minitest' gem 'minitest-emoji' end The #group method allows multiple groups. The #gemspec development dependencies are placed in the :development group by default. This may be overridden with the :development_group option: gemspec development_group: :other EOF PLATFORMS = <<-'EOF'.freeze RubyGems platforms are composed of three parts, a CPU, an OS, and a version. These values are taken from values in rbconfig.rb. You can view your current platform by running `gem environment`. RubyGems matches platforms as follows: * The CPU must match exactly unless one of the platforms has "universal" as the CPU or the local CPU starts with "arm" and the gem's CPU is exactly "arm" (for gems that support generic ARM architecture). * The OS must match exactly. * The versions must match exactly unless one of the versions is nil. For commands that install, uninstall and list gems, you can override what RubyGems thinks your platform is with the --platform option. The platform you pass must match "#{cpu}-#{os}" or "#{cpu}-#{os}-#{version}". On mswin platforms, the version is the compiler version, not the OS version. (Ruby compiled with VC6 uses "60" as the compiler version, VC8 uses "80".) For the ARM architecture, gems with a platform of "arm-linux" should run on a reasonable set of ARM CPUs and not depend on instructions present on a limited subset of the architecture. For example, the binary should run on platforms armv5, armv6hf, armv6l, armv7, etc. If you use the "arm-linux" platform please test your gem on a variety of ARM hardware before release to ensure it functions correctly. Example platforms: x86-freebsd # Any FreeBSD version on an x86 CPU universal-darwin-8 # Darwin 8 only gems that run on any CPU x86-mswin32-80 # Windows gems compiled with VC8 armv7-linux # Gem complied for an ARMv7 CPU running linux arm-linux # Gem compiled for any ARM CPU running linux When building platform gems, set the platform in the gem specification to Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's platform. EOF # NOTE when updating also update Gem::Command::HELP SUBCOMMANDS = [ ["commands", :show_commands], ["options", Gem::Command::HELP], ["examples", EXAMPLES], ["gem_dependencies", GEM_DEPENDENCIES], ["platforms", PLATFORMS], ].freeze # :startdoc: def initialize super 'help', "Provide help on the 'gem' command" @command_manager = Gem::CommandManager.instance end def usage # :nodoc: "#{program_name} ARGUMENT" end def execute arg = options[:args][0] _, help = SUBCOMMANDS.find do |command,| begins? command, arg end if help if Symbol === help send help else say help end return end if options[:help] show_help elsif arg show_command_help arg else say Gem::Command::HELP end end def show_commands # :nodoc: out = [] out << "GEM commands are:" out << nil margin_width = 4 desc_width = @command_manager.command_names.map {|n| n.size }.max + 4 summary_width = 80 - margin_width - desc_width wrap_indent = ' ' * (margin_width + desc_width) format = "#{' ' * margin_width}%-#{desc_width}s%s" @command_manager.command_names.each do |cmd_name| command = @command_manager[cmd_name] next if command.deprecated? summary = if command command.summary else "[No command found for #{cmd_name}]" end summary = wrap(summary, summary_width).split "\n" out << sprintf(format, cmd_name, summary.shift) until summary.empty? do out << "#{wrap_indent}#{summary.shift}" end end out << nil out << "For help on a particular command, use 'gem help COMMAND'." out << nil out << "Commands may be abbreviated, so long as they are unambiguous." out << "e.g. 'gem i rake' is short for 'gem install rake'." say out.join("\n") end def show_command_help(command_name) # :nodoc: command_name = command_name.downcase possibilities = @command_manager.find_command_possibilities command_name if possibilities.size == 1 command = @command_manager[possibilities.first] command.invoke("--help") elsif possibilities.size > 1 alert_warning "Ambiguous command #{command_name} (#{possibilities.join(', ')})" else alert_warning "Unknown command #{command_name}. Try: gem help commands" end end end rubygems/rubygems/commands/check_command.rb 0000644 00000004364 15102661023 0015142 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../version_option' require_relative '../validator' require_relative '../doctor' class Gem::Commands::CheckCommand < Gem::Command include Gem::VersionOption def initialize super 'check', 'Check a gem repository for added or missing files', :alien => true, :doctor => false, :dry_run => false, :gems => true add_option('-a', '--[no-]alien', 'Report "unmanaged" or rogue files in the', 'gem repository') do |value, options| options[:alien] = value end add_option('--[no-]doctor', 'Clean up uninstalled gems and broken', 'specifications') do |value, options| options[:doctor] = value end add_option('--[no-]dry-run', 'Do not remove files, only report what', 'would be removed') do |value, options| options[:dry_run] = value end add_option('--[no-]gems', 'Check installed gems for problems') do |value, options| options[:gems] = value end add_version_option 'check' end def check_gems say 'Checking gems...' say gems = get_all_gem_names rescue [] Gem::Validator.new.alien(gems).sort.each do |key, val| unless val.empty? say "#{key} has #{val.size} problems" val.each do |error_entry| say " #{error_entry.path}:" say " #{error_entry.problem}" end else say "#{key} is error-free" if Gem.configuration.verbose end say end end def doctor say 'Checking for files from uninstalled gems...' say Gem.path.each do |gem_repo| doctor = Gem::Doctor.new gem_repo, options[:dry_run] doctor.doctor end end def execute check_gems if options[:gems] doctor if options[:doctor] end def arguments # :nodoc: 'GEMNAME name of gem to check' end def defaults_str # :nodoc: '--gems --alien' end def description # :nodoc: <<-EOF The check command can list and repair problems with installed gems and specifications and will clean up gems that have been partially uninstalled. EOF end def usage # :nodoc: "#{program_name} [OPTIONS] [GEMNAME ...]" end end rubygems/rubygems/commands/lock_command.rb 0000644 00000005304 15102661023 0015010 0 ustar 00 # frozen_string_literal: true require_relative '../command' class Gem::Commands::LockCommand < Gem::Command def initialize super 'lock', 'Generate a lockdown list of gems', :strict => false add_option '-s', '--[no-]strict', 'fail if unable to satisfy a dependency' do |strict, options| options[:strict] = strict end end def arguments # :nodoc: "GEMNAME name of gem to lock\nVERSION version of gem to lock" end def defaults_str # :nodoc: "--no-strict" end def description # :nodoc: <<-EOF The lock command will generate a list of +gem+ statements that will lock down the versions for the gem given in the command line. It will specify exact versions in the requirements list to ensure that the gems loaded will always be consistent. A full recursive search of all effected gems will be generated. Example: gem lock rails-1.0.0 > lockdown.rb will produce in lockdown.rb: require "rubygems" gem 'rails', '= 1.0.0' gem 'rake', '= 0.7.0.1' gem 'activesupport', '= 1.2.5' gem 'activerecord', '= 1.13.2' gem 'actionpack', '= 1.11.2' gem 'actionmailer', '= 1.1.5' gem 'actionwebservice', '= 1.0.0' Just load lockdown.rb from your application to ensure that the current versions are loaded. Make sure that lockdown.rb is loaded *before* any other require statements. Notice that rails 1.0.0 only requires that rake 0.6.2 or better be used. Rake-0.7.0.1 is the most recent version installed that satisfies that, so we lock it down to the exact version. EOF end def usage # :nodoc: "#{program_name} GEMNAME-VERSION [GEMNAME-VERSION ...]" end def complain(message) if options[:strict] raise Gem::Exception, message else say "# #{message}" end end def execute say "require 'rubygems'" locked = {} pending = options[:args] until pending.empty? do full_name = pending.shift spec = Gem::Specification.load spec_path(full_name) if spec.nil? complain "Could not find gem #{full_name}, try using the full name" next end say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name] locked[spec.name] = true spec.runtime_dependencies.each do |dep| next if locked[dep.name] candidates = dep.matching_specs if candidates.empty? complain "Unable to satisfy '#{dep}' from currently installed gems" else pending << candidates.last.full_name end end end end def spec_path(gem_full_name) gemspecs = Gem.path.map do |path| File.join path, "specifications", "#{gem_full_name}.gemspec" end gemspecs.find {|path| File.exist? path } end end rubygems/rubygems/commands/update_command.rb 0000644 00000022115 15102661023 0015341 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../command_manager' require_relative '../dependency_installer' require_relative '../install_update_options' require_relative '../local_remote_options' require_relative '../spec_fetcher' require_relative '../version_option' require_relative '../install_message' # must come before rdoc for messaging require_relative '../rdoc' class Gem::Commands::UpdateCommand < Gem::Command include Gem::InstallUpdateOptions include Gem::LocalRemoteOptions include Gem::VersionOption attr_reader :installer # :nodoc: attr_reader :updated # :nodoc: def initialize super 'update', 'Update installed gems to the latest version', :document => %w[rdoc ri], :force => false add_install_update_options Gem::OptionParser.accept Gem::Version do |value| Gem::Version.new value value end add_option('--system [VERSION]', Gem::Version, 'Update the RubyGems system software') do |value, options| value = true unless value options[:system] = value end add_local_remote_options add_platform_option add_prerelease_option "as update targets" @updated = [] @installer = nil end def arguments # :nodoc: "GEMNAME name of gem to update" end def defaults_str # :nodoc: "--document --no-force --install-dir #{Gem.dir}" end def description # :nodoc: <<-EOF The update command will update your gems to the latest version. The update command does not remove the previous version. Use the cleanup command to remove old versions. EOF end def usage # :nodoc: "#{program_name} GEMNAME [GEMNAME ...]" end def check_latest_rubygems(version) # :nodoc: if Gem.rubygems_version == version say "Latest version already installed. Done." terminate_interaction end end def check_oldest_rubygems(version) # :nodoc: if oldest_supported_version > version alert_error "rubygems #{version} is not supported on #{RUBY_VERSION}. The oldest version supported by this ruby is #{oldest_supported_version}" terminate_interaction 1 end end def check_update_arguments # :nodoc: unless options[:args].empty? alert_error "Gem names are not allowed with the --system option" terminate_interaction 1 end end def execute if options[:system] update_rubygems return end gems_to_update = which_to_update( highest_installed_gems, options[:args].uniq ) if options[:explain] say "Gems to update:" gems_to_update.each do |name_tuple| say " #{name_tuple.full_name}" end return end say "Updating installed gems" updated = update_gems gems_to_update updated_names = updated.map {|spec| spec.name } not_updated_names = options[:args].uniq - updated_names if updated.empty? say "Nothing to update" else say "Gems updated: #{updated_names.join(' ')}" say "Gems already up-to-date: #{not_updated_names.join(' ')}" unless not_updated_names.empty? end end def fetch_remote_gems(spec) # :nodoc: dependency = Gem::Dependency.new spec.name, "> #{spec.version}" dependency.prerelease = options[:prerelease] fetcher = Gem::SpecFetcher.fetcher spec_tuples, errors = fetcher.search_for_dependency dependency error = errors.find {|e| e.respond_to? :exception } raise error if error spec_tuples end def highest_installed_gems # :nodoc: hig = {} # highest installed gems # Get only gem specifications installed as --user-install Gem::Specification.dirs = Gem.user_dir if options[:user_install] Gem::Specification.each do |spec| if hig[spec.name].nil? or hig[spec.name].version < spec.version hig[spec.name] = spec end end hig end def highest_remote_name_tuple(spec) # :nodoc: spec_tuples = fetch_remote_gems spec matching_gems = spec_tuples.select do |g,_| g.name == spec.name and g.match_platform? end highest_remote_gem = matching_gems.max highest_remote_gem ||= [Gem::NameTuple.null] highest_remote_gem.first end def install_rubygems(version) # :nodoc: args = update_rubygems_arguments update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}" Dir.chdir update_dir do say "Installing RubyGems #{version}" unless options[:silent] installed = preparing_gem_layout_for(version) do system Gem.ruby, '--disable-gems', 'setup.rb', *args end say "RubyGems system software updated" if installed unless options[:silent] end end def preparing_gem_layout_for(version) if Gem::Version.new(version) >= Gem::Version.new("3.2.a") yield else require "tmpdir" tmpdir = Dir.mktmpdir FileUtils.mv Gem.plugindir, tmpdir status = yield if status FileUtils.rm_rf tmpdir else FileUtils.mv File.join(tmpdir, "plugins"), Gem.plugindir end status end end def rubygems_target_version version = options[:system] update_latest = version == true if update_latest version = Gem::Version.new Gem::VERSION requirement = Gem::Requirement.new ">= #{Gem::VERSION}" else version = Gem::Version.new version requirement = Gem::Requirement.new version end rubygems_update = Gem::Specification.new rubygems_update.name = 'rubygems-update' rubygems_update.version = version hig = { 'rubygems-update' => rubygems_update, } gems_to_update = which_to_update hig, options[:args], :system up_ver = gems_to_update.first.version target = if update_latest up_ver else version end return target, requirement end def update_gem(name, version = Gem::Requirement.default) return if @updated.any? {|spec| spec.name == name } update_options = options.dup update_options[:prerelease] = version.prerelease? @installer = Gem::DependencyInstaller.new update_options say "Updating #{name}" unless options[:system] && options[:silent] begin @installer.install name, Gem::Requirement.new(version) rescue Gem::InstallError, Gem::DependencyError => e alert_error "Error installing #{name}:\n\t#{e.message}" end @installer.installed_gems.each do |spec| @updated << spec end end def update_gems(gems_to_update) gems_to_update.uniq.sort.each do |name_tuple| update_gem name_tuple.name, name_tuple.version end @updated end ## # Update RubyGems software to the latest version. def update_rubygems if Gem.disable_system_update_message alert_error Gem.disable_system_update_message terminate_interaction 1 end check_update_arguments version, requirement = rubygems_target_version check_latest_rubygems version check_oldest_rubygems version update_gem 'rubygems-update', version installed_gems = Gem::Specification.find_all_by_name 'rubygems-update', requirement version = installed_gems.first.version install_rubygems version end def update_rubygems_arguments # :nodoc: args = [] args << '--silent' if options[:silent] args << '--prefix' << Gem.prefix if Gem.prefix args << '--no-document' unless options[:document].include?('rdoc') || options[:document].include?('ri') args << '--no-format-executable' if options[:no_format_executable] args << '--previous-version' << Gem::VERSION if options[:system] == true or Gem::Version.new(options[:system]) >= Gem::Version.new(2) args end def which_to_update(highest_installed_gems, gem_names, system = false) result = [] highest_installed_gems.each do |l_name, l_spec| next if not gem_names.empty? and gem_names.none? {|name| name == l_spec.name } highest_remote_tup = highest_remote_name_tuple l_spec highest_remote_ver = highest_remote_tup.version highest_installed_ver = l_spec.version if system or (highest_installed_ver < highest_remote_ver) result << Gem::NameTuple.new(l_spec.name, [highest_installed_ver, highest_remote_ver].max, highest_remote_tup.platform) end end result end private # # Oldest version we support downgrading to. This is the version that # originally ships with the first patch version of each ruby, because we never # test each ruby against older rubygems, so we can't really guarantee it # works. Version list can be checked here: https://stdgems.org/rubygems # def oldest_supported_version @oldest_supported_version ||= if Gem.ruby_version > Gem::Version.new("3.0.a") Gem::Version.new("3.2.3") elsif Gem.ruby_version > Gem::Version.new("2.7.a") Gem::Version.new("3.1.2") elsif Gem.ruby_version > Gem::Version.new("2.6.a") Gem::Version.new("3.0.1") elsif Gem.ruby_version > Gem::Version.new("2.5.a") Gem::Version.new("2.7.3") elsif Gem.ruby_version > Gem::Version.new("2.4.a") Gem::Version.new("2.6.8") else Gem::Version.new("2.5.2") end end end rubygems/rubygems/commands/dependency_command.rb 0000644 00000012220 15102661023 0016171 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../local_remote_options' require_relative '../version_option' class Gem::Commands::DependencyCommand < Gem::Command include Gem::LocalRemoteOptions include Gem::VersionOption def initialize super 'dependency', 'Show the dependencies of an installed gem', :version => Gem::Requirement.default, :domain => :local add_version_option add_platform_option add_prerelease_option add_option('-R', '--[no-]reverse-dependencies', 'Include reverse dependencies in the output') do |value, options| options[:reverse_dependencies] = value end add_option('-p', '--pipe', "Pipe Format (name --version ver)") do |value, options| options[:pipe_format] = value end add_local_remote_options end def arguments # :nodoc: "REGEXP show dependencies for gems whose names start with REGEXP" end def defaults_str # :nodoc: "--local --version '#{Gem::Requirement.default}' --no-reverse-dependencies" end def description # :nodoc: <<-EOF The dependency commands lists which other gems a given gem depends on. For local gems only the reverse dependencies can be shown (which gems depend on the named gem). The dependency list can be displayed in a format suitable for piping for use with other commands. EOF end def usage # :nodoc: "#{program_name} REGEXP" end def fetch_remote_specs(dependency) # :nodoc: fetcher = Gem::SpecFetcher.fetcher ss, = fetcher.spec_for_dependency dependency ss.map {|spec, _| spec } end def fetch_specs(name_pattern, dependency) # :nodoc: specs = [] if local? specs.concat Gem::Specification.stubs.find_all {|spec| name_pattern =~ spec.name and dependency.requirement.satisfied_by? spec.version }.map(&:to_spec) end specs.concat fetch_remote_specs dependency if remote? ensure_specs specs specs.uniq.sort end def gem_dependency(pattern, version, prerelease) # :nodoc: dependency = Gem::Deprecate.skip_during do Gem::Dependency.new pattern, version end dependency.prerelease = prerelease dependency end def display_pipe(specs) # :nodoc: specs.each do |spec| unless spec.dependencies.empty? spec.dependencies.sort_by {|dep| dep.name }.each do |dep| say "#{dep.name} --version '#{dep.requirement}'" end end end end def display_readable(specs, reverse) # :nodoc: response = String.new specs.each do |spec| response << print_dependencies(spec) unless reverse[spec.full_name].empty? response << " Used by\n" reverse[spec.full_name].each do |sp, dep| response << " #{sp} (#{dep})\n" end end response << "\n" end say response end def execute ensure_local_only_reverse_dependencies pattern = name_pattern options[:args] dependency = gem_dependency pattern, options[:version], options[:prerelease] specs = fetch_specs pattern, dependency reverse = reverse_dependencies specs if options[:pipe_format] display_pipe specs else display_readable specs, reverse end end def ensure_local_only_reverse_dependencies # :nodoc: if options[:reverse_dependencies] and remote? and not local? alert_error 'Only reverse dependencies for local gems are supported.' terminate_interaction 1 end end def ensure_specs(specs) # :nodoc: return unless specs.empty? patterns = options[:args].join ',' say "No gems found matching #{patterns} (#{options[:version]})" if Gem.configuration.verbose terminate_interaction 1 end def print_dependencies(spec, level = 0) # :nodoc: response = String.new response << ' ' * level + "Gem #{spec.full_name}\n" unless spec.dependencies.empty? spec.dependencies.sort_by {|dep| dep.name }.each do |dep| response << ' ' * level + " #{dep}\n" end end response end def remote_specs(dependency) # :nodoc: fetcher = Gem::SpecFetcher.fetcher ss, _ = fetcher.spec_for_dependency dependency ss.map {|s,o| s } end def reverse_dependencies(specs) # :nodoc: reverse = Hash.new {|h, k| h[k] = [] } return reverse unless options[:reverse_dependencies] specs.each do |spec| reverse[spec.full_name] = find_reverse_dependencies spec end reverse end ## # Returns an Array of [specification, dep] that are satisfied by +spec+. def find_reverse_dependencies(spec) # :nodoc: result = [] Gem::Specification.each do |sp| sp.dependencies.each do |dep| dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep if spec.name == dep.name and dep.requirement.satisfied_by?(spec.version) result << [sp.full_name, dep] end end end result end private def name_pattern(args) args << '' if args.empty? if args.length == 1 and args.first =~ /\A(.*)(i)?\z/m flags = $2 ? Regexp::IGNORECASE : nil Regexp.new $1, flags else /\A#{Regexp.union(*args)}/ end end end rubygems/rubygems/commands/search_command.rb 0000644 00000001735 15102661023 0015331 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../query_utils' class Gem::Commands::SearchCommand < Gem::Command include Gem::QueryUtils def initialize super 'search', 'Display remote gems whose name matches REGEXP', :name => //, :domain => :remote, :details => false, :versions => true, :installed => nil, :version => Gem::Requirement.default add_query_options end def arguments # :nodoc: "REGEXP regexp to search for in gem name" end def defaults_str # :nodoc: "--remote --no-details" end def description # :nodoc: <<-EOF The search command displays remote gems whose name matches the given regexp. The --details option displays additional details from the gem but will take a little longer to complete as it must download the information individually from the index. To list local gems use the list command. EOF end def usage # :nodoc: "#{program_name} [REGEXP]" end end rubygems/rubygems/commands/query_command.rb 0000644 00000002371 15102661023 0015226 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../query_utils' require_relative '../deprecate' class Gem::Commands::QueryCommand < Gem::Command extend Gem::Deprecate rubygems_deprecate_command include Gem::QueryUtils alias warning_without_suggested_alternatives deprecation_warning def deprecation_warning warning_without_suggested_alternatives message = "It is recommended that you use `gem search` or `gem list` instead.\n" alert_warning message unless Gem::Deprecate.skip end def initialize(name = 'query', summary = 'Query gem information in local or remote repositories') super name, summary, :name => //, :domain => :local, :details => false, :versions => true, :installed => nil, :version => Gem::Requirement.default add_option('-n', '--name-matches REGEXP', 'Name of gem(s) to query on matches the', 'provided REGEXP') do |value, options| options[:name] = /#{value}/i end add_query_options end def description # :nodoc: <<-EOF The query command is the basis for the list and search commands. You should really use the list and search commands instead. This command is too hard to use. EOF end end rubygems/rubygems/commands/specification_command.rb 0000644 00000006655 15102661023 0016712 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../local_remote_options' require_relative '../version_option' require_relative '../package' class Gem::Commands::SpecificationCommand < Gem::Command include Gem::LocalRemoteOptions include Gem::VersionOption def initialize Gem.load_yaml super 'specification', 'Display gem specification (in yaml)', :domain => :local, :version => Gem::Requirement.default, :format => :yaml add_version_option('examine') add_platform_option add_prerelease_option add_option('--all', 'Output specifications for all versions of', 'the gem') do |value, options| options[:all] = true end add_option('--ruby', 'Output ruby format') do |value, options| options[:format] = :ruby end add_option('--yaml', 'Output YAML format') do |value, options| options[:format] = :yaml end add_option('--marshal', 'Output Marshal format') do |value, options| options[:format] = :marshal end add_local_remote_options end def arguments # :nodoc: <<-ARGS GEMFILE name of gem to show the gemspec for FIELD name of gemspec field to show ARGS end def defaults_str # :nodoc: "--local --version '#{Gem::Requirement.default}' --yaml" end def description # :nodoc: <<-EOF The specification command allows you to extract the specification from a gem for examination. The specification can be output in YAML, ruby or Marshal formats. Specific fields in the specification can be extracted in YAML format: $ gem spec rake summary --- Ruby based make-like utility. ... EOF end def usage # :nodoc: "#{program_name} [GEMFILE] [FIELD]" end def execute specs = [] gem = options[:args].shift unless gem raise Gem::CommandLineError, "Please specify a gem name or file on the command line" end case v = options[:version] when String req = Gem::Requirement.create v when Gem::Requirement req = v else raise Gem::CommandLineError, "Unsupported version type: '#{v}'" end if !req.none? and options[:all] alert_error "Specify --all or -v, not both" terminate_interaction 1 end if options[:all] dep = Gem::Dependency.new gem else dep = Gem::Dependency.new gem, req end field = get_one_optional_argument raise Gem::CommandLineError, "--ruby and FIELD are mutually exclusive" if field and options[:format] == :ruby if local? if File.exist? gem specs << Gem::Package.new(gem).spec rescue nil end if specs.empty? specs.push(*dep.matching_specs) end end if remote? dep.prerelease = options[:prerelease] found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep specs.push(*found.map {|spec,| spec }) end if specs.empty? alert_error "No gem matching '#{dep}' found" terminate_interaction 1 end platform = get_platform_from_requirements(options) if platform specs = specs.select{|s| s.platform.to_s == platform } end unless options[:all] specs = [specs.max_by {|s| s.version }] end specs.each do |s| s = s.send field if field say case options[:format] when :ruby then s.to_ruby when :marshal then Marshal.dump s else s.to_yaml end say "\n" end end end rubygems/rubygems/commands/pristine_command.rb 0000644 00000013766 15102661023 0015730 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../package' require_relative '../installer' require_relative '../version_option' class Gem::Commands::PristineCommand < Gem::Command include Gem::VersionOption def initialize super 'pristine', 'Restores installed gems to pristine condition from files located in the gem cache', :version => Gem::Requirement.default, :extensions => true, :extensions_set => false, :all => false add_option('--all', 'Restore all installed gems to pristine', 'condition') do |value, options| options[:all] = value end add_option('--skip=gem_name', 'used on --all, skip if name == gem_name') do |value, options| options[:skip] ||= [] options[:skip] << value end add_option('--[no-]extensions', 'Restore gems with extensions', 'in addition to regular gems') do |value, options| options[:extensions_set] = true options[:extensions] = value end add_option('--only-executables', 'Only restore executables') do |value, options| options[:only_executables] = value end add_option('--only-plugins', 'Only restore plugins') do |value, options| options[:only_plugins] = value end add_option('-E', '--[no-]env-shebang', 'Rewrite executables with a shebang', 'of /usr/bin/env') do |value, options| options[:env_shebang] = value end add_option('-i', '--install-dir DIR', 'Gem repository to get binstubs and plugins installed') do |value, options| options[:install_dir] = File.expand_path(value) end add_option('-n', '--bindir DIR', 'Directory where executables are', 'located') do |value, options| options[:bin_dir] = File.expand_path(value) end add_version_option('restore to', 'pristine condition') end def arguments # :nodoc: "GEMNAME gem to restore to pristine condition (unless --all)" end def defaults_str # :nodoc: '--extensions' end def description # :nodoc: <<-EOF The pristine command compares an installed gem with the contents of its cached .gem file and restores any files that don't match the cached .gem's copy. If you have made modifications to an installed gem, the pristine command will revert them. All extensions are rebuilt and all bin stubs for the gem are regenerated after checking for modifications. If the cached gem cannot be found it will be downloaded. If --no-extensions is provided pristine will not attempt to restore a gem with an extension. If --extensions is given (but not --all or gem names) only gems with extensions will be restored. EOF end def usage # :nodoc: "#{program_name} [GEMNAME ...]" end def execute specs = if options[:all] Gem::Specification.map # `--extensions` must be explicitly given to pristine only gems # with extensions. elsif options[:extensions_set] and options[:extensions] and options[:args].empty? Gem::Specification.select do |spec| spec.extensions and not spec.extensions.empty? end else get_all_gem_names.sort.map do |gem_name| Gem::Specification.find_all_by_name(gem_name, options[:version]).reverse end.flatten end specs = specs.select{|spec| RUBY_ENGINE == spec.platform || Gem::Platform.local === spec.platform || spec.platform == Gem::Platform::RUBY } if specs.to_a.empty? raise Gem::Exception, "Failed to find gems #{options[:args]} #{options[:version]}" end say "Restoring gems to pristine condition..." specs.each do |spec| if spec.default_gem? say "Skipped #{spec.full_name}, it is a default gem" next end if options.has_key? :skip if options[:skip].include? spec.name say "Skipped #{spec.full_name}, it was given through options" next end end unless spec.extensions.empty? or options[:extensions] or options[:only_executables] or options[:only_plugins] say "Skipped #{spec.full_name}, it needs to compile an extension" next end gem = spec.cache_file unless File.exist? gem or options[:only_executables] or options[:only_plugins] require_relative '../remote_fetcher' say "Cached gem for #{spec.full_name} not found, attempting to fetch..." dep = Gem::Dependency.new spec.name, spec.version found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep if found.empty? say "Skipped #{spec.full_name}, it was not found from cache and remote sources" next end spec_candidate, source = found.first Gem::RemoteFetcher.fetcher.download spec_candidate, source.uri.to_s, spec.base_dir end env_shebang = if options.include? :env_shebang options[:env_shebang] else install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install'] install_defaults.to_s['--env-shebang'] end bin_dir = options[:bin_dir] if options[:bin_dir] install_dir = options[:install_dir] if options[:install_dir] installer_options = { :wrappers => true, :force => true, :install_dir => install_dir || spec.base_dir, :env_shebang => env_shebang, :build_args => spec.build_args, :bin_dir => bin_dir, } if options[:only_executables] installer = Gem::Installer.for_spec(spec, installer_options) installer.generate_bin elsif options[:only_plugins] installer = Gem::Installer.for_spec(spec, installer_options) installer.generate_plugins else installer = Gem::Installer.at(gem, installer_options) installer.install end say "Restored #{spec.full_name}" end end end rubygems/rubygems/commands/which_command.rb 0000644 00000004142 15102661023 0015161 0 ustar 00 # frozen_string_literal: true require_relative '../command' class Gem::Commands::WhichCommand < Gem::Command def initialize super 'which', 'Find the location of a library file you can require', :search_gems_first => false, :show_all => false add_option '-a', '--[no-]all', 'show all matching files' do |show_all, options| options[:show_all] = show_all end add_option '-g', '--[no-]gems-first', 'search gems before non-gems' do |gems_first, options| options[:search_gems_first] = gems_first end end def arguments # :nodoc: "FILE name of file to find" end def defaults_str # :nodoc: "--no-gems-first --no-all" end def description # :nodoc: <<-EOF The which command is like the shell which command and shows you where the file you wish to require lives. You can use the which command to help determine why you are requiring a version you did not expect or to look at the content of a file you are requiring to see why it does not behave as you expect. EOF end def execute found = true options[:args].each do |arg| arg = arg.sub(/#{Regexp.union(*Gem.suffixes)}$/, '') dirs = $LOAD_PATH spec = Gem::Specification.find_by_path arg if spec if options[:search_gems_first] dirs = spec.full_require_paths + $LOAD_PATH else dirs = $LOAD_PATH + spec.full_require_paths end end paths = find_paths arg, dirs if paths.empty? alert_error "Can't find Ruby library file or shared library #{arg}" found = false else say paths end end terminate_interaction 1 unless found end def find_paths(package_name, dirs) result = [] dirs.each do |dir| Gem.suffixes.each do |ext| full_path = File.join dir, "#{package_name}#{ext}" if File.exist? full_path and not File.directory? full_path result << full_path return result unless options[:show_all] end end end result end def usage # :nodoc: "#{program_name} FILE [FILE ...]" end end rubygems/rubygems/commands/signin_command.rb 0000644 00000001577 15102661023 0015357 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../gemcutter_utilities' class Gem::Commands::SigninCommand < Gem::Command include Gem::GemcutterUtilities def initialize super 'signin', 'Sign in to any gemcutter-compatible host. '\ 'It defaults to https://rubygems.org' add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options| options[:host] = value end add_otp_option end def description # :nodoc: 'The signin command executes host sign in for a push server (the default is'\ ' https://rubygems.org). The host can be provided with the host flag or can'\ ' be inferred from the provided gem. Host resolution matches the resolution'\ ' strategy for the push command.' end def usage # :nodoc: program_name end def execute sign_in options[:host] end end rubygems/rubygems/commands/environment_command.rb 0000644 00000012055 15102661023 0016425 0 ustar 00 # frozen_string_literal: true require_relative '../command' class Gem::Commands::EnvironmentCommand < Gem::Command def initialize super 'environment', 'Display information about the RubyGems environment' end def arguments # :nodoc: args = <<-EOF gemdir display the path where gems are installed gempath display path used to search for gems version display the gem format version remotesources display the remote gem servers platform display the supported gem platforms <omitted> display everything EOF return args.gsub(/^\s+/, '') end def description # :nodoc: <<-EOF The environment command lets you query rubygems for its configuration for use in shell scripts or as a debugging aid. The RubyGems environment can be controlled through command line arguments, gemrc files, environment variables and built-in defaults. Command line argument defaults and some RubyGems defaults can be set in a ~/.gemrc file for individual users and a gemrc in the SYSTEM CONFIGURATION DIRECTORY for all users. These files are YAML files with the following YAML keys: :sources: A YAML array of remote gem repositories to install gems from :verbose: Verbosity of the gem command. false, true, and :really are the levels :update_sources: Enable/disable automatic updating of repository metadata :backtrace: Print backtrace when RubyGems encounters an error :gempath: The paths in which to look for gems :disable_default_gem_server: Force specification of gem server host on push <gem_command>: A string containing arguments for the specified gem command Example: :verbose: false install: --no-wrappers update: --no-wrappers :disable_default_gem_server: true RubyGems' default local repository can be overridden with the GEM_PATH and GEM_HOME environment variables. GEM_HOME sets the default repository to install into. GEM_PATH allows multiple local repositories to be searched for gems. If you are behind a proxy server, RubyGems uses the HTTP_PROXY, HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the proxy server. If you would like to push gems to a private gem server the RUBYGEMS_HOST environment variable can be set to the URI for that server. If you are packaging RubyGems all of RubyGems' defaults are in lib/rubygems/defaults.rb. You may override these in lib/rubygems/defaults/operating_system.rb EOF end def usage # :nodoc: "#{program_name} [arg]" end def execute out = String.new arg = options[:args][0] out << case arg when /^version/ then Gem::VERSION when /^gemdir/, /^gemhome/, /^home/, /^GEM_HOME/ then Gem.dir when /^gempath/, /^path/, /^GEM_PATH/ then Gem.path.join(File::PATH_SEPARATOR) when /^remotesources/ then Gem.sources.to_a.join("\n") when /^platform/ then Gem.platforms.join(File::PATH_SEPARATOR) when nil then show_environment else raise Gem::CommandLineError, "Unknown environment option [#{arg}]" end say out true end def add_path(out, path) path.each do |component| out << " - #{component}\n" end end def show_environment # :nodoc: out = "RubyGems Environment:\n".dup out << " - RUBYGEMS VERSION: #{Gem::VERSION}\n" out << " - RUBY VERSION: #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}" out << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL out << ") [#{RUBY_PLATFORM}]\n" out << " - INSTALLATION DIRECTORY: #{Gem.dir}\n" out << " - USER INSTALLATION DIRECTORY: #{Gem.user_dir}\n" out << " - RUBYGEMS PREFIX: #{Gem.prefix}\n" unless Gem.prefix.nil? out << " - RUBY EXECUTABLE: #{Gem.ruby}\n" out << " - GIT EXECUTABLE: #{git_path}\n" out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n" out << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n" out << " - SYSTEM CONFIGURATION DIRECTORY: #{Gem::ConfigFile::SYSTEM_CONFIG_PATH}\n" out << " - RUBYGEMS PLATFORMS:\n" Gem.platforms.each do |platform| out << " - #{platform}\n" end out << " - GEM PATHS:\n" out << " - #{Gem.dir}\n" gem_path = Gem.path.dup gem_path.delete Gem.dir add_path out, gem_path out << " - GEM CONFIGURATION:\n" Gem.configuration.each do |name, value| value = value.gsub(/./, '*') if name == 'gemcutter_key' out << " - #{name.inspect} => #{value.inspect}\n" end out << " - REMOTE SOURCES:\n" Gem.sources.each do |s| out << " - #{s}\n" end out << " - SHELL PATH:\n" shell_path = ENV['PATH'].split(File::PATH_SEPARATOR) add_path out, shell_path out end private ## # Git binary path def git_path exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""] ENV["PATH"].split(File::PATH_SEPARATOR).each do |path| exts.each do |ext| exe = File.join(path, "git#{ext}") return exe if File.executable?(exe) && !File.directory?(exe) end end return nil end end rubygems/rubygems/commands/contents_command.rb 0000644 00000010074 15102661023 0015715 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../version_option' class Gem::Commands::ContentsCommand < Gem::Command include Gem::VersionOption def initialize super 'contents', 'Display the contents of the installed gems', :specdirs => [], :lib_only => false, :prefix => true, :show_install_dir => false add_version_option add_option('--all', "Contents for all gems") do |all, options| options[:all] = all end add_option('-s', '--spec-dir a,b,c', Array, "Search for gems under specific paths") do |spec_dirs, options| options[:specdirs] = spec_dirs end add_option('-l', '--[no-]lib-only', "Only return files in the Gem's lib_dirs") do |lib_only, options| options[:lib_only] = lib_only end add_option('--[no-]prefix', "Don't include installed path prefix") do |prefix, options| options[:prefix] = prefix end add_option('--[no-]show-install-dir', 'Show only the gem install dir') do |show, options| options[:show_install_dir] = show end @path_kind = nil @spec_dirs = nil @version = nil end def arguments # :nodoc: "GEMNAME name of gem to list contents for" end def defaults_str # :nodoc: "--no-lib-only --prefix" end def description # :nodoc: <<-EOF The contents command lists the files in an installed gem. The listing can be given as full file names, file names without the installed directory prefix or only the files that are requireable. EOF end def usage # :nodoc: "#{program_name} GEMNAME [GEMNAME ...]" end def execute @version = options[:version] || Gem::Requirement.default @spec_dirs = specification_directories @path_kind = path_description @spec_dirs names = gem_names names.each do |name| found = if options[:show_install_dir] gem_install_dir name else gem_contents name end terminate_interaction 1 unless found or names.length > 1 end end def files_in(spec) if spec.default_gem? files_in_default_gem spec else files_in_gem spec end end def files_in_gem(spec) gem_path = spec.full_gem_path extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only] glob = "#{gem_path}#{extra}/**/*" prefix_re = /#{Regexp.escape(gem_path)}\// Dir[glob].map do |file| [gem_path, file.sub(prefix_re, "")] end end def files_in_default_gem(spec) spec.files.map do |file| case file when /\A#{spec.bindir}\// # $' is POSTMATCH [RbConfig::CONFIG['bindir'], $'] when /\.so\z/ [RbConfig::CONFIG['archdir'], file] else [RbConfig::CONFIG['rubylibdir'], file] end end end def gem_contents(name) spec = spec_for name return false unless spec files = files_in spec show_files files true end def gem_install_dir(name) spec = spec_for name return false unless spec say spec.gem_dir true end def gem_names # :nodoc: if options[:all] Gem::Specification.map(&:name) else get_all_gem_names end end def path_description(spec_dirs) # :nodoc: if spec_dirs.empty? "default gem paths" else "specified path" end end def show_files(files) files.sort.each do |prefix, basename| absolute_path = File.join(prefix, basename) next if File.directory? absolute_path if options[:prefix] say absolute_path else say basename end end end def spec_for(name) spec = Gem::Specification.find_all_by_name(name, @version).first return spec if spec say "Unable to find gem '#{name}' in #{@path_kind}" if Gem.configuration.verbose say "\nDirectories searched:" @spec_dirs.sort.each {|dir| say dir } end return nil end def specification_directories # :nodoc: options[:specdirs].map do |i| [i, File.join(i, "specifications")] end.flatten end end rubygems/rubygems/commands/install_command.rb 0000644 00000017004 15102661023 0015526 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../install_update_options' require_relative '../dependency_installer' require_relative '../local_remote_options' require_relative '../validator' require_relative '../version_option' ## # Gem installer command line tool # # See `gem help install` class Gem::Commands::InstallCommand < Gem::Command attr_reader :installed_specs # :nodoc: include Gem::VersionOption include Gem::LocalRemoteOptions include Gem::InstallUpdateOptions def initialize defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ :format_executable => false, :lock => true, :suggest_alternate => true, :version => Gem::Requirement.default, :without_groups => [], }) super 'install', 'Install a gem into the local repository', defaults add_install_update_options add_local_remote_options add_platform_option add_version_option add_prerelease_option "to be installed. (Only for listed gems)" @installed_specs = [] end def arguments # :nodoc: "GEMNAME name of gem to install" end def defaults_str # :nodoc: "--both --version '#{Gem::Requirement.default}' --document --no-force\n" + "--install-dir #{Gem.dir} --lock" end def description # :nodoc: <<-EOF The install command installs local or remote gem into a gem repository. For gems with executables ruby installs a wrapper file into the executable directory by default. This can be overridden with the --no-wrappers option. The wrapper allows you to choose among alternate gem versions using _version_. For example `rake _0.7.3_ --version` will run rake version 0.7.3 if a newer version is also installed. Gem Dependency Files ==================== RubyGems can install a consistent set of gems across multiple environments using `gem install -g` when a gem dependencies file (gem.deps.rb, Gemfile or Isolate) is present. If no explicit file is given RubyGems attempts to find one in the current directory. When the RUBYGEMS_GEMDEPS environment variable is set to a gem dependencies file the gems from that file will be activated at startup time. Set it to a specific filename or to "-" to have RubyGems automatically discover the gem dependencies file by walking up from the current directory. NOTE: Enabling automatic discovery on multiuser systems can lead to execution of arbitrary code when used from directories outside your control. Extension Install Failures ========================== If an extension fails to compile during gem installation the gem specification is not written out, but the gem remains unpacked in the repository. You may need to specify the path to the library's headers and libraries to continue. You can do this by adding a -- between RubyGems' options and the extension's build options: $ gem install some_extension_gem [build fails] Gem files will remain installed in \\ /path/to/gems/some_extension_gem-1.0 for inspection. Results logged to /path/to/gems/some_extension_gem-1.0/gem_make.out $ gem install some_extension_gem -- --with-extension-lib=/path/to/lib [build succeeds] $ gem list some_extension_gem *** LOCAL GEMS *** some_extension_gem (1.0) $ If you correct the compilation errors by editing the gem files you will need to write the specification by hand. For example: $ gem install some_extension_gem [build fails] Gem files will remain installed in \\ /path/to/gems/some_extension_gem-1.0 for inspection. Results logged to /path/to/gems/some_extension_gem-1.0/gem_make.out $ [cd /path/to/gems/some_extension_gem-1.0] $ [edit files or what-have-you and run make] $ gem spec ../../cache/some_extension_gem-1.0.gem --ruby > \\ ../../specifications/some_extension_gem-1.0.gemspec $ gem list some_extension_gem *** LOCAL GEMS *** some_extension_gem (1.0) $ Command Alias ========================== You can use `i` command instead of `install`. $ gem i GEMNAME EOF end def usage # :nodoc: "#{program_name} [options] GEMNAME [GEMNAME ...] -- --build-flags" end def check_install_dir # :nodoc: if options[:install_dir] and options[:user_install] alert_error "Use --install-dir or --user-install but not both" terminate_interaction 1 end end def check_version # :nodoc: if options[:version] != Gem::Requirement.default and get_all_gem_names.size > 1 alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \ " version requirements using `gem install 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`" terminate_interaction 1 end end def execute if options.include? :gemdeps install_from_gemdeps return # not reached end @installed_specs = [] ENV.delete 'GEM_PATH' if options[:install_dir].nil? check_install_dir check_version load_hooks exit_code = install_gems show_installed terminate_interaction exit_code end def install_from_gemdeps # :nodoc: require_relative '../request_set' rs = Gem::RequestSet.new specs = rs.install_from_gemdeps options do |req, inst| s = req.full_spec if inst say "Installing #{s.name} (#{s.version})" else say "Using #{s.name} (#{s.version})" end end @installed_specs = specs terminate_interaction end def install_gem(name, version) # :nodoc: return if options[:conservative] and not Gem::Dependency.new(name, version).matching_specs.empty? req = Gem::Requirement.create(version) dinst = Gem::DependencyInstaller.new options request_set = dinst.resolve_dependencies name, req if options[:explain] say "Gems to install:" request_set.sorted_requests.each do |activation_request| say " #{activation_request.full_name}" end else @installed_specs.concat request_set.install options end show_install_errors dinst.errors end def install_gems # :nodoc: exit_code = 0 get_all_gem_names_and_versions.each do |gem_name, gem_version| gem_version ||= options[:version] domain = options[:domain] domain = :local unless options[:suggest_alternate] suppress_suggestions = (domain == :local) begin install_gem gem_name, gem_version rescue Gem::InstallError => e alert_error "Error installing #{gem_name}:\n\t#{e.message}" exit_code |= 1 rescue Gem::GemNotFoundException => e show_lookup_failure e.name, e.version, e.errors, suppress_suggestions exit_code |= 2 rescue Gem::UnsatisfiableDependencyError => e show_lookup_failure e.name, e.version, e.errors, suppress_suggestions, "'#{gem_name}' (#{gem_version})" exit_code |= 2 end end exit_code end ## # Loads post-install hooks def load_hooks # :nodoc: if options[:install_as_default] require_relative '../install_default_message' else require_relative '../install_message' end require_relative '../rdoc' end def show_install_errors(errors) # :nodoc: return unless errors errors.each do |x| return unless Gem::SourceFetchProblem === x require_relative "../uri" msg = "Unable to pull data from '#{Gem::Uri.new(x.source.uri).redacted}': #{x.error.message}" alert_warning msg end end def show_installed # :nodoc: return if @installed_specs.empty? gems = @installed_specs.length == 1 ? 'gem' : 'gems' say "#{@installed_specs.length} #{gems} installed" end end rubygems/rubygems/commands/stale_command.rb 0000644 00000001704 15102661023 0015170 0 ustar 00 # frozen_string_literal: true require_relative '../command' class Gem::Commands::StaleCommand < Gem::Command def initialize super('stale', 'List gems along with access times') end def description # :nodoc: <<-EOF The stale command lists the latest access time for all the files in your installed gems. You can use this command to discover gems and gem versions you are no longer using. EOF end def usage # :nodoc: "#{program_name}" end def execute gem_to_atime = {} Gem::Specification.each do |spec| name = spec.full_name Dir["#{spec.full_gem_path}/**/*.*"].each do |file| next if File.directory?(file) stat = File.stat(file) gem_to_atime[name] ||= stat.atime gem_to_atime[name] = stat.atime if gem_to_atime[name] < stat.atime end end gem_to_atime.sort_by {|_, atime| atime }.each do |name, atime| say "#{name} at #{atime.strftime '%c'}" end end end rubygems/rubygems/commands/build_command.rb 0000644 00000005662 15102661023 0015166 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../package' require_relative '../version_option' class Gem::Commands::BuildCommand < Gem::Command include Gem::VersionOption def initialize super 'build', 'Build a gem from a gemspec' add_platform_option add_option '--force', 'skip validation of the spec' do |value, options| options[:force] = true end add_option '--strict', 'consider warnings as errors when validating the spec' do |value, options| options[:strict] = true end add_option '-o', '--output FILE', 'output gem with the given filename' do |value, options| options[:output] = value end add_option '-C PATH', 'Run as if gem build was started in <PATH> instead of the current working directory.' do |value, options| options[:build_path] = value end end def arguments # :nodoc: "GEMSPEC_FILE gemspec file name to build a gem for" end def description # :nodoc: <<-EOF The build command allows you to create a gem from a ruby gemspec. The best way to build a gem is to use a Rakefile and the Gem::PackageTask which ships with RubyGems. The gemspec can either be created by hand or extracted from an existing gem with gem spec: $ gem unpack my_gem-1.0.gem Unpacked gem: '.../my_gem-1.0' $ gem spec my_gem-1.0.gem --ruby > my_gem-1.0/my_gem-1.0.gemspec $ cd my_gem-1.0 [edit gem contents] $ gem build my_gem-1.0.gemspec Gems can be saved to a specified filename with the output option: $ gem build my_gem-1.0.gemspec --output=release.gem EOF end def usage # :nodoc: "#{program_name} GEMSPEC_FILE" end def execute if build_path = options[:build_path] Dir.chdir(build_path) { build_gem } return end build_gem end private def find_gemspec(glob = "*.gemspec") gemspecs = Dir.glob(glob).sort if gemspecs.size > 1 alert_error "Multiple gemspecs found: #{gemspecs}, please specify one" terminate_interaction(1) end gemspecs.first end def build_gem gemspec = resolve_gem_name if gemspec build_package(gemspec) else alert_error error_message terminate_interaction(1) end end def build_package(gemspec) spec = Gem::Specification.load(gemspec) if spec Gem::Package.build( spec, options[:force], options[:strict], options[:output] ) else alert_error "Error loading gemspec. Aborting." terminate_interaction 1 end end def resolve_gem_name return find_gemspec unless gem_name if File.exist?(gem_name) gem_name else find_gemspec("#{gem_name}.gemspec") || find_gemspec(gem_name) end end def error_message if gem_name "Couldn't find a gemspec file matching '#{gem_name}' in #{Dir.pwd}" else "Couldn't find a gemspec file in #{Dir.pwd}" end end def gem_name get_one_optional_argument end end rubygems/rubygems/commands/rdoc_command.rb 0000644 00000004753 15102661023 0015016 0 ustar 00 # frozen_string_literal: true require_relative '../command' require_relative '../version_option' require_relative '../rdoc' require 'fileutils' class Gem::Commands::RdocCommand < Gem::Command include Gem::VersionOption def initialize super 'rdoc', 'Generates RDoc for pre-installed gems', :version => Gem::Requirement.default, :include_rdoc => false, :include_ri => true, :overwrite => false add_option('--all', 'Generate RDoc/RI documentation for all', 'installed gems') do |value, options| options[:all] = value end add_option('--[no-]rdoc', 'Generate RDoc HTML') do |value, options| options[:include_rdoc] = value end add_option('--[no-]ri', 'Generate RI data') do |value, options| options[:include_ri] = value end add_option('--[no-]overwrite', 'Overwrite installed documents') do |value, options| options[:overwrite] = value end add_version_option end def arguments # :nodoc: "GEMNAME gem to generate documentation for (unless --all)" end def defaults_str # :nodoc: "--version '#{Gem::Requirement.default}' --ri --no-overwrite" end def description # :nodoc: <<-DESC The rdoc command builds documentation for installed gems. By default only documentation is built using rdoc, but additional types of documentation may be built through rubygems plugins and the Gem.post_installs hook. Use --overwrite to force rebuilding of documentation. DESC end def usage # :nodoc: "#{program_name} [args]" end def execute specs = if options[:all] Gem::Specification.to_a else get_all_gem_names.map do |name| Gem::Specification.find_by_name name, options[:version] end.flatten.uniq end if specs.empty? alert_error 'No matching gems found' terminate_interaction 1 end specs.each do |spec| doc = Gem::RDoc.new spec, options[:include_rdoc], options[:include_ri] doc.force = options[:overwrite] if options[:overwrite] FileUtils.rm_rf File.join(spec.doc_dir, 'ri') FileUtils.rm_rf File.join(spec.doc_dir, 'rdoc') end begin doc.generate rescue Errno::ENOENT => e e.message =~ / - / alert_error "Unable to document #{spec.full_name}, #{$'} is missing, skipping" terminate_interaction 1 if specs.length == 1 end end end end rubygems/rubygems/commands/mirror_command.rb 0000644 00000001162 15102661023 0015370 0 ustar 00 # frozen_string_literal: true require_relative '../command' unless defined? Gem::Commands::MirrorCommand class Gem::Commands::MirrorCommand < Gem::Command def initialize super('mirror', 'Mirror all gem files (requires rubygems-mirror)') begin Gem::Specification.find_by_name('rubygems-mirror').activate rescue Gem::LoadError # no-op end end def description # :nodoc: <<-EOF The mirror command has been moved to the rubygems-mirror gem. EOF end def execute alert_error "Install the rubygems-mirror gem for the mirror command" end end end rubygems/rubygems/install_default_message.rb 0000644 00000000534 15102661023 0015437 0 ustar 00 # frozen_string_literal: true require_relative '../rubygems' require_relative 'user_interaction' ## # A post-install hook that displays "Successfully installed # some_gem-1.0 as a default gem" Gem.post_install do |installer| ui = Gem::DefaultUserInteraction.ui ui.say "Successfully installed #{installer.spec.full_name} as a default gem" end rubygems/rubygems/requirement.rb 0000644 00000015642 15102661023 0013127 0 ustar 00 # frozen_string_literal: true require_relative "version" ## # A Requirement is a set of one or more version restrictions. It supports a # few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators. # # See Gem::Version for a description on how versions and requirements work # together in RubyGems. class Gem::Requirement OPS = { #:nodoc: "=" => lambda {|v, r| v == r }, "!=" => lambda {|v, r| v != r }, ">" => lambda {|v, r| v > r }, "<" => lambda {|v, r| v < r }, ">=" => lambda {|v, r| v >= r }, "<=" => lambda {|v, r| v <= r }, "~>" => lambda {|v, r| v >= r && v.release < r.bump }, }.freeze SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc: quoted = OPS.keys.map {|k| Regexp.quote k }.join "|" PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*".freeze # :nodoc: ## # A regular expression that matches a requirement PATTERN = /\A#{PATTERN_RAW}\z/.freeze ## # The default requirement matches any non-prerelease version DefaultRequirement = [">=", Gem::Version.new(0)].freeze ## # The default requirement matches any version DefaultPrereleaseRequirement = [">=", Gem::Version.new("0.a")].freeze ## # Raised when a bad requirement is encountered class BadRequirementError < ArgumentError; end ## # Factory method to create a Gem::Requirement object. Input may be # a Version, a String, or nil. Intended to simplify client code. # # If the input is "weird", the default version requirement is # returned. def self.create(*inputs) return new inputs if inputs.length > 1 input = inputs.shift case input when Gem::Requirement then input when Gem::Version, Array then new input when '!' then source_set else if input.respond_to? :to_str new [input.to_str] else default end end end def self.default new '>= 0' end def self.default_prerelease new '>= 0.a' end ### # A source set requirement, used for Gemfiles and lockfiles def self.source_set # :nodoc: SOURCE_SET_REQUIREMENT end ## # Parse +obj+, returning an <tt>[op, version]</tt> pair. +obj+ can # be a String or a Gem::Version. # # If +obj+ is a String, it can be either a full requirement # specification, like <tt>">= 1.2"</tt>, or a simple version number, # like <tt>"1.2"</tt>. # # parse("> 1.0") # => [">", Gem::Version.new("1.0")] # parse("1.0") # => ["=", Gem::Version.new("1.0")] # parse(Gem::Version.new("1.0")) # => ["=, Gem::Version.new("1.0")] def self.parse(obj) return ["=", obj] if Gem::Version === obj unless PATTERN =~ obj.to_s raise BadRequirementError, "Illformed requirement [#{obj.inspect}]" end if $1 == ">=" && $2 == "0" DefaultRequirement elsif $1 == ">=" && $2 == "0.a" DefaultPrereleaseRequirement else [-($1 || "="), Gem::Version.new($2)] end end ## # An array of requirement pairs. The first element of the pair is # the op, and the second is the Gem::Version. attr_reader :requirements #:nodoc: ## # Constructs a requirement from +requirements+. Requirements can be # Strings, Gem::Versions, or Arrays of those. +nil+ and duplicate # requirements are ignored. An empty set of +requirements+ is the # same as <tt>">= 0"</tt>. def initialize(*requirements) requirements = requirements.flatten requirements.compact! requirements.uniq! if requirements.empty? @requirements = [DefaultRequirement] else @requirements = requirements.map! {|r| self.class.parse r } end end ## # Concatenates the +new+ requirements onto this requirement. def concat(new) new = new.flatten new.compact! new.uniq! new = new.map {|r| self.class.parse r } @requirements.concat new end ## # Formats this requirement for use in a Gem::RequestSet::Lockfile. def for_lockfile # :nodoc: return if [DefaultRequirement] == @requirements list = requirements.sort_by do |_, version| version end.map do |op, version| "#{op} #{version}" end.uniq " (#{list.join ', '})" end ## # true if this gem has no requirements. def none? if @requirements.size == 1 @requirements[0] == DefaultRequirement else false end end ## # true if the requirement is for only an exact version def exact? return false unless @requirements.size == 1 @requirements[0][0] == "=" end def as_list # :nodoc: requirements.map {|op, version| "#{op} #{version}" } end def hash # :nodoc: requirements.map {|r| r.first == "~>" ? [r[0], r[1].to_s] : r }.sort.hash end def marshal_dump # :nodoc: [@requirements] end def marshal_load(array) # :nodoc: @requirements = array[0] raise TypeError, "wrong @requirements" unless Array === @requirements end def yaml_initialize(tag, vals) # :nodoc: vals.each do |ivar, val| instance_variable_set "@#{ivar}", val end end def init_with(coder) # :nodoc: yaml_initialize coder.tag, coder.map end def to_yaml_properties # :nodoc: ["@requirements"] end def encode_with(coder) # :nodoc: coder.add 'requirements', @requirements end ## # A requirement is a prerelease if any of the versions inside of it # are prereleases def prerelease? requirements.any? {|r| r.last.prerelease? } end def pretty_print(q) # :nodoc: q.group 1, 'Gem::Requirement.new(', ')' do q.pp as_list end end ## # True if +version+ satisfies this Requirement. def satisfied_by?(version) raise ArgumentError, "Need a Gem::Version: #{version.inspect}" unless Gem::Version === version requirements.all? {|op, rv| OPS[op].call version, rv } end alias :=== :satisfied_by? alias :=~ :satisfied_by? ## # True if the requirement will not always match the latest version. def specific? return true if @requirements.length > 1 # GIGO, > 1, > 2 is silly not %w[> >=].include? @requirements.first.first # grab the operator end def to_s # :nodoc: as_list.join ", " end def ==(other) # :nodoc: return unless Gem::Requirement === other # An == check is always necessary return false unless _sorted_requirements == other._sorted_requirements # An == check is sufficient unless any requirements use ~> return true unless _tilde_requirements.any? # If any requirements use ~> we use the stricter `#eql?` that also checks # that version precision is the same _tilde_requirements.eql?(other._tilde_requirements) end protected def _sorted_requirements @_sorted_requirements ||= requirements.sort_by(&:to_s) end def _tilde_requirements @_tilde_requirements ||= _sorted_requirements.select {|r| r.first == "~>" } end end class Gem::Version # This is needed for compatibility with older yaml # gemspecs. Requirement = Gem::Requirement # :nodoc: end rubygems/rubygems/util/list.rb 0000644 00000001111 15102661023 0012501 0 ustar 00 # frozen_string_literal: true module Gem class List include Enumerable attr_accessor :value, :tail def initialize(value = nil, tail = nil) @value = value @tail = tail end def each n = self while n yield n.value n = n.tail end end def to_a super.reverse end def prepend(value) List.new value, self end def pretty_print(q) # :nodoc: q.pp to_a end def self.prepend(list, value) return List.new(value) unless list List.new value, list end end end rubygems/rubygems/util/licenses.rb 0000644 00000021571 15102661023 0013347 0 ustar 00 # frozen_string_literal: true require_relative '../text' class Gem::Licenses extend Gem::Text NONSTANDARD = 'Nonstandard'.freeze LICENSE_REF = 'LicenseRef-.+'.freeze # Software Package Data Exchange (SPDX) standard open-source software # license identifiers LICENSE_IDENTIFIERS = %w[ 0BSD AAL ADSL AFL-1.1 AFL-1.2 AFL-2.0 AFL-2.1 AFL-3.0 AGPL-1.0 AGPL-1.0-only AGPL-1.0-or-later AGPL-3.0 AGPL-3.0-only AGPL-3.0-or-later AMDPLPA AML AMPAS ANTLR-PD ANTLR-PD-fallback APAFML APL-1.0 APSL-1.0 APSL-1.1 APSL-1.2 APSL-2.0 Abstyles Adobe-2006 Adobe-Glyph Afmparse Aladdin Apache-1.0 Apache-1.1 Apache-2.0 Artistic-1.0 Artistic-1.0-Perl Artistic-1.0-cl8 Artistic-2.0 BSD-1-Clause BSD-2-Clause BSD-2-Clause-FreeBSD BSD-2-Clause-NetBSD BSD-2-Clause-Patent BSD-2-Clause-Views BSD-3-Clause BSD-3-Clause-Attribution BSD-3-Clause-Clear BSD-3-Clause-LBNL BSD-3-Clause-Modification BSD-3-Clause-No-Military-License BSD-3-Clause-No-Nuclear-License BSD-3-Clause-No-Nuclear-License-2014 BSD-3-Clause-No-Nuclear-Warranty BSD-3-Clause-Open-MPI BSD-4-Clause BSD-4-Clause-Shortened BSD-4-Clause-UC BSD-Protection BSD-Source-Code BSL-1.0 BUSL-1.1 Bahyph Barr Beerware BitTorrent-1.0 BitTorrent-1.1 BlueOak-1.0.0 Borceux C-UDA-1.0 CAL-1.0 CAL-1.0-Combined-Work-Exception CATOSL-1.1 CC-BY-1.0 CC-BY-2.0 CC-BY-2.5 CC-BY-3.0 CC-BY-3.0-AT CC-BY-3.0-US CC-BY-4.0 CC-BY-NC-1.0 CC-BY-NC-2.0 CC-BY-NC-2.5 CC-BY-NC-3.0 CC-BY-NC-4.0 CC-BY-NC-ND-1.0 CC-BY-NC-ND-2.0 CC-BY-NC-ND-2.5 CC-BY-NC-ND-3.0 CC-BY-NC-ND-3.0-IGO CC-BY-NC-ND-4.0 CC-BY-NC-SA-1.0 CC-BY-NC-SA-2.0 CC-BY-NC-SA-2.5 CC-BY-NC-SA-3.0 CC-BY-NC-SA-4.0 CC-BY-ND-1.0 CC-BY-ND-2.0 CC-BY-ND-2.5 CC-BY-ND-3.0 CC-BY-ND-4.0 CC-BY-SA-1.0 CC-BY-SA-2.0 CC-BY-SA-2.0-UK CC-BY-SA-2.1-JP CC-BY-SA-2.5 CC-BY-SA-3.0 CC-BY-SA-3.0-AT CC-BY-SA-4.0 CC-PDDC CC0-1.0 CDDL-1.0 CDDL-1.1 CDL-1.0 CDLA-Permissive-1.0 CDLA-Sharing-1.0 CECILL-1.0 CECILL-1.1 CECILL-2.0 CECILL-2.1 CECILL-B CECILL-C CERN-OHL-1.1 CERN-OHL-1.2 CERN-OHL-P-2.0 CERN-OHL-S-2.0 CERN-OHL-W-2.0 CNRI-Jython CNRI-Python CNRI-Python-GPL-Compatible CPAL-1.0 CPL-1.0 CPOL-1.02 CUA-OPL-1.0 Caldera ClArtistic Condor-1.1 Crossword CrystalStacker Cube D-FSL-1.0 DOC DRL-1.0 DSDP Dotseqn ECL-1.0 ECL-2.0 EFL-1.0 EFL-2.0 EPICS EPL-1.0 EPL-2.0 EUDatagrid EUPL-1.0 EUPL-1.1 EUPL-1.2 Entessa ErlPL-1.1 Eurosym FSFAP FSFUL FSFULLR FTL Fair Frameworx-1.0 FreeBSD-DOC FreeImage GD GFDL-1.1 GFDL-1.1-invariants-only GFDL-1.1-invariants-or-later GFDL-1.1-no-invariants-only GFDL-1.1-no-invariants-or-later GFDL-1.1-only GFDL-1.1-or-later GFDL-1.2 GFDL-1.2-invariants-only GFDL-1.2-invariants-or-later GFDL-1.2-no-invariants-only GFDL-1.2-no-invariants-or-later GFDL-1.2-only GFDL-1.2-or-later GFDL-1.3 GFDL-1.3-invariants-only GFDL-1.3-invariants-or-later GFDL-1.3-no-invariants-only GFDL-1.3-no-invariants-or-later GFDL-1.3-only GFDL-1.3-or-later GL2PS GLWTPL GPL-1.0 GPL-1.0+ GPL-1.0-only GPL-1.0-or-later GPL-2.0 GPL-2.0+ GPL-2.0-only GPL-2.0-or-later GPL-2.0-with-GCC-exception GPL-2.0-with-autoconf-exception GPL-2.0-with-bison-exception GPL-2.0-with-classpath-exception GPL-2.0-with-font-exception GPL-3.0 GPL-3.0+ GPL-3.0-only GPL-3.0-or-later GPL-3.0-with-GCC-exception GPL-3.0-with-autoconf-exception Giftware Glide Glulxe HPND HPND-sell-variant HTMLTIDY HaskellReport Hippocratic-2.1 IBM-pibs ICU IJG IPA IPL-1.0 ISC ImageMagick Imlib2 Info-ZIP Intel Intel-ACPI Interbase-1.0 JPNIC JSON JasPer-2.0 LAL-1.2 LAL-1.3 LGPL-2.0 LGPL-2.0+ LGPL-2.0-only LGPL-2.0-or-later LGPL-2.1 LGPL-2.1+ LGPL-2.1-only LGPL-2.1-or-later LGPL-3.0 LGPL-3.0+ LGPL-3.0-only LGPL-3.0-or-later LGPLLR LPL-1.0 LPL-1.02 LPPL-1.0 LPPL-1.1 LPPL-1.2 LPPL-1.3a LPPL-1.3c Latex2e Leptonica LiLiQ-P-1.1 LiLiQ-R-1.1 LiLiQ-Rplus-1.1 Libpng Linux-OpenIB MIT MIT-0 MIT-CMU MIT-Modern-Variant MIT-advertising MIT-enna MIT-feh MIT-open-group MITNFA MPL-1.0 MPL-1.1 MPL-2.0 MPL-2.0-no-copyleft-exception MS-PL MS-RL MTLL MakeIndex MirOS Motosoto MulanPSL-1.0 MulanPSL-2.0 Multics Mup NAIST-2003 NASA-1.3 NBPL-1.0 NCGL-UK-2.0 NCSA NGPL NIST-PD NIST-PD-fallback NLOD-1.0 NLPL NOSL NPL-1.0 NPL-1.1 NPOSL-3.0 NRL NTP NTP-0 Naumen Net-SNMP NetCDF Newsletr Nokia Noweb Nunit O-UDA-1.0 OCCT-PL OCLC-2.0 ODC-By-1.0 ODbL-1.0 OFL-1.0 OFL-1.0-RFN OFL-1.0-no-RFN OFL-1.1 OFL-1.1-RFN OFL-1.1-no-RFN OGC-1.0 OGDL-Taiwan-1.0 OGL-Canada-2.0 OGL-UK-1.0 OGL-UK-2.0 OGL-UK-3.0 OGTSL OLDAP-1.1 OLDAP-1.2 OLDAP-1.3 OLDAP-1.4 OLDAP-2.0 OLDAP-2.0.1 OLDAP-2.1 OLDAP-2.2 OLDAP-2.2.1 OLDAP-2.2.2 OLDAP-2.3 OLDAP-2.4 OLDAP-2.5 OLDAP-2.6 OLDAP-2.7 OLDAP-2.8 OML OPL-1.0 OSET-PL-2.1 OSL-1.0 OSL-1.1 OSL-2.0 OSL-2.1 OSL-3.0 OpenSSL PDDL-1.0 PHP-3.0 PHP-3.01 PSF-2.0 Parity-6.0.0 Parity-7.0.0 Plexus PolyForm-Noncommercial-1.0.0 PolyForm-Small-Business-1.0.0 PostgreSQL Python-2.0 QPL-1.0 Qhull RHeCos-1.1 RPL-1.1 RPL-1.5 RPSL-1.0 RSA-MD RSCPL Rdisc Ruby SAX-PD SCEA SGI-B-1.0 SGI-B-1.1 SGI-B-2.0 SHL-0.5 SHL-0.51 SISSL SISSL-1.2 SMLNJ SMPPL SNIA SPL-1.0 SSH-OpenSSH SSH-short SSPL-1.0 SWL Saxpath Sendmail Sendmail-8.23 SimPL-2.0 Sleepycat Spencer-86 Spencer-94 Spencer-99 StandardML-NJ SugarCRM-1.1.3 TAPR-OHL-1.0 TCL TCP-wrappers TMate TORQUE-1.1 TOSL TU-Berlin-1.0 TU-Berlin-2.0 UCL-1.0 UPL-1.0 Unicode-DFS-2015 Unicode-DFS-2016 Unicode-TOU Unlicense VOSTROM VSL-1.0 Vim W3C W3C-19980720 W3C-20150513 WTFPL Watcom-1.0 Wsuipa X11 XFree86-1.1 XSkat Xerox Xnet YPL-1.0 YPL-1.1 ZPL-1.1 ZPL-2.0 ZPL-2.1 Zed Zend-2.0 Zimbra-1.3 Zimbra-1.4 Zlib blessing bzip2-1.0.5 bzip2-1.0.6 copyleft-next-0.3.0 copyleft-next-0.3.1 curl diffmark dvipdfm eCos-2.0 eGenix etalab-2.0 gSOAP-1.3b gnuplot iMatix libpng-2.0 libselinux-1.0 libtiff mpich2 psfrag psutils wxWindows xinetd xpp zlib-acknowledgement ].freeze # exception identifiers EXCEPTION_IDENTIFIERS = %w[ 389-exception Autoconf-exception-2.0 Autoconf-exception-3.0 Bison-exception-2.2 Bootloader-exception CLISP-exception-2.0 Classpath-exception-2.0 DigiRule-FOSS-exception FLTK-exception Fawkes-Runtime-exception Font-exception-2.0 GCC-exception-2.0 GCC-exception-3.1 GPL-3.0-linking-exception GPL-3.0-linking-source-exception GPL-CC-1.0 LGPL-3.0-linking-exception LLVM-exception LZMA-exception Libtool-exception Linux-syscall-note Nokia-Qt-exception-1.1 OCCT-exception-1.0 OCaml-LGPL-linking-exception OpenJDK-assembly-exception-1.0 PS-or-PDF-font-exception-20170817 Qt-GPL-exception-1.0 Qt-LGPL-exception-1.1 Qwt-exception-1.0 SHL-2.0 SHL-2.1 Swift-exception Universal-FOSS-exception-1.0 WxWindows-exception-3.1 eCos-exception-2.0 freertos-exception-2.0 gnu-javamail-exception i2p-gpl-java-exception mif-exception openvpn-openssl-exception u-boot-exception-2.0 ].freeze REGEXP = %r{ \A (?: #{Regexp.union(LICENSE_IDENTIFIERS)} \+? (?:\s WITH \s #{Regexp.union(EXCEPTION_IDENTIFIERS)})? | #{NONSTANDARD} | #{LICENSE_REF} ) \Z }ox.freeze def self.match?(license) !REGEXP.match(license).nil? end def self.suggestions(license) by_distance = LICENSE_IDENTIFIERS.group_by do |identifier| levenshtein_distance(identifier, license) end lowest = by_distance.keys.min return unless lowest < license.size by_distance[lowest] end end rubygems/rubygems/rdoc.rb 0000644 00000000347 15102661023 0011512 0 ustar 00 # frozen_string_literal: true require_relative '../rubygems' begin require 'rdoc/rubygems_hook' module Gem RDoc = ::RDoc::RubygemsHook end Gem.done_installing(&Gem::RDoc.method(:generation_hook)) rescue LoadError end rubygems/rubygems/text.rb 0000644 00000003546 15102661023 0011553 0 ustar 00 # frozen_string_literal: true ## # A collection of text-wrangling methods module Gem::Text ## # Remove any non-printable characters and make the text suitable for # printing. def clean_text(text) text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".".freeze) end def truncate_text(text, description, max_length = 100_000) raise ArgumentError, "max_length must be positive" unless max_length > 0 return text if text.size <= max_length "Truncating #{description} to #{max_length.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse} characters:\n" + text[0, max_length] end ## # Wraps +text+ to +wrap+ characters and optionally indents by +indent+ # characters def format_text(text, wrap, indent=0) result = [] work = clean_text(text) while work.length > wrap do if work =~ /^(.{0,#{wrap}})[ \n]/ result << $1.rstrip work.slice!(0, $&.length) else result << work.slice!(0, wrap) end end result << work if work.length.nonzero? result.join("\n").gsub(/^/, " " * indent) end def min3(a, b, c) # :nodoc: if a < b && a < c a elsif b < c b else c end end # This code is based directly on the Text gem implementation # Returns a value representing the "cost" of transforming str1 into str2 def levenshtein_distance(str1, str2) s = str1 t = str2 n = s.length m = t.length return m if (0 == n) return n if (0 == m) d = (0..m).to_a x = nil str1.each_char.each_with_index do |char1,i| e = i + 1 str2.each_char.each_with_index do |char2,j| cost = (char1 == char2) ? 0 : 1 x = min3( d[j + 1] + 1, # insertion e + 1, # deletion d[j] + cost # substitution ) d[j] = e e = x end d[m] = x end return x end end rubygems/rubygems/gemcutter_utilities.rb 0000644 00000017202 15102661023 0014653 0 ustar 00 # frozen_string_literal: true require_relative 'remote_fetcher' require_relative 'text' ## # Utility methods for using the RubyGems API. module Gem::GemcutterUtilities ERROR_CODE = 1 API_SCOPES = %i[index_rubygems push_rubygem yank_rubygem add_owner remove_owner access_webhooks show_dashboard].freeze include Gem::Text attr_writer :host attr_writer :scope ## # Add the --key option def add_key_option add_option('-k', '--key KEYNAME', Symbol, 'Use the given API key', "from #{Gem.configuration.credentials_path}") do |value,options| options[:key] = value end end ## # Add the --otp option def add_otp_option add_option('--otp CODE', 'Digit code for multifactor authentication', 'You can also use the environment variable GEM_HOST_OTP_CODE') do |value, options| options[:otp] = value end end ## # The API key from the command options or from the user's configuration. def api_key if ENV["GEM_HOST_API_KEY"] ENV["GEM_HOST_API_KEY"] elsif options[:key] verify_api_key options[:key] elsif Gem.configuration.api_keys.key?(host) Gem.configuration.api_keys[host] else Gem.configuration.rubygems_api_key end end ## # The OTP code from the command options or from the user's configuration. def otp options[:otp] || ENV["GEM_HOST_OTP_CODE"] end ## # The host to connect to either from the RUBYGEMS_HOST environment variable # or from the user's configuration def host configured_host = Gem.host unless Gem.configuration.disable_default_gem_server @host ||= begin env_rubygems_host = ENV['RUBYGEMS_HOST'] env_rubygems_host = nil if env_rubygems_host and env_rubygems_host.empty? env_rubygems_host || configured_host end end ## # Creates an RubyGems API to +host+ and +path+ with the given HTTP +method+. # # If +allowed_push_host+ metadata is present, then it will only allow that host. def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, &block) require 'net/http' self.host = host if host unless self.host alert_error "You must specify a gem server" terminate_interaction(ERROR_CODE) end if allowed_push_host allowed_host_uri = URI.parse(allowed_push_host) host_uri = URI.parse(self.host) unless (host_uri.scheme == allowed_host_uri.scheme) && (host_uri.host == allowed_host_uri.host) alert_error "#{self.host.inspect} is not allowed by the gemspec, which only allows #{allowed_push_host.inspect}" terminate_interaction(ERROR_CODE) end end uri = URI.parse "#{self.host}/#{path}" response = request_with_otp(method, uri, &block) if mfa_unauthorized?(response) ask_otp response = request_with_otp(method, uri, &block) end if api_key_forbidden?(response) update_scope(scope) request_with_otp(method, uri, &block) else response end end def mfa_unauthorized?(response) response.kind_of?(Net::HTTPUnauthorized) && response.body.start_with?('You have enabled multifactor authentication') end def update_scope(scope) sign_in_host = self.host pretty_host = pretty_host(sign_in_host) update_scope_params = { scope => true } say "The existing key doesn't have access of #{scope} on #{pretty_host}. Please sign in to update access." email = ask " Email: " password = ask_for_password "Password: " response = rubygems_api_request(:put, "api/v1/api_key", sign_in_host, scope: scope) do |request| request.basic_auth email, password request["OTP"] = otp if otp request.body = URI.encode_www_form({:api_key => api_key }.merge(update_scope_params)) end with_response response do |resp| say "Added #{scope} scope to the existing API key" end end ## # Signs in with the RubyGems API at +sign_in_host+ and sets the rubygems API # key. def sign_in(sign_in_host = nil, scope: nil) sign_in_host ||= self.host return if api_key pretty_host = pretty_host(sign_in_host) say "Enter your #{pretty_host} credentials." say "Don't have an account yet? " + "Create one at #{sign_in_host}/sign_up" email = ask " Email: " password = ask_for_password "Password: " say "\n" key_name = get_key_name(scope) scope_params = get_scope_params(scope) response = rubygems_api_request(:post, "api/v1/api_key", sign_in_host, scope: scope) do |request| request.basic_auth email, password request["OTP"] = otp if otp request.body = URI.encode_www_form({ name: key_name }.merge(scope_params)) end with_response response do |resp| say "Signed in with API key: #{key_name}." set_api_key host, resp.body end end ## # Retrieves the pre-configured API key +key+ or terminates interaction with # an error. def verify_api_key(key) if Gem.configuration.api_keys.key? key Gem.configuration.api_keys[key] else alert_error "No such API key. Please add it to your configuration (done automatically on initial `gem push`)." terminate_interaction(ERROR_CODE) end end ## # If +response+ is an HTTP Success (2XX) response, yields the response if a # block was given or shows the response body to the user. # # If the response was not successful, shows an error to the user including # the +error_prefix+ and the response body. def with_response(response, error_prefix = nil) case response when Net::HTTPSuccess then if block_given? yield response else say clean_text(response.body) end else message = response.body message = "#{error_prefix}: #{message}" if error_prefix say clean_text(message) terminate_interaction(ERROR_CODE) end end ## # Returns true when the user has enabled multifactor authentication from # +response+ text and no otp provided by options. def set_api_key(host, key) if host == Gem::DEFAULT_HOST Gem.configuration.rubygems_api_key = key else Gem.configuration.set_api_key host, key end end private def request_with_otp(method, uri, &block) request_method = Net::HTTP.const_get method.to_s.capitalize Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req| req["OTP"] = otp if otp block.call(req) end end def ask_otp say 'You have enabled multi-factor authentication. Please enter OTP code.' options[:otp] = ask 'Code: ' end def pretty_host(host) if Gem::DEFAULT_HOST == host 'RubyGems.org' else host end end def get_scope_params(scope) scope_params = {} if scope scope_params = { scope => true } else say "Please select scopes you want to enable for the API key (y/n)" API_SCOPES.each do |scope| selected = ask "#{scope} [y/N]: " scope_params[scope] = true if selected =~ /^[yY](es)?$/ end say "\n" end scope_params end def get_key_name(scope) hostname = Socket.gethostname || "unknown-host" user = ENV["USER"] || ENV["USERNAME"] || "unknown-user" ts = Time.now.strftime("%Y%m%d%H%M%S") default_key_name = "#{hostname}-#{user}-#{ts}" key_name = ask "API Key name [#{default_key_name}]: " unless scope if key_name.nil? || key_name.empty? default_key_name else key_name end end def api_key_forbidden?(response) response.kind_of?(Net::HTTPForbidden) && response.body.start_with?("The API key doesn't have access") end end rubygems/rubygems/exceptions.rb 0000644 00000014543 15102661023 0012747 0 ustar 00 # frozen_string_literal: true require_relative 'deprecate' ## # Base exception class for RubyGems. All exception raised by RubyGems are a # subclass of this one. class Gem::Exception < RuntimeError; end class Gem::CommandLineError < Gem::Exception; end class Gem::DependencyError < Gem::Exception; end class Gem::DependencyRemovalException < Gem::Exception; end ## # Raised by Gem::Resolver when a Gem::Dependency::Conflict reaches the # toplevel. Indicates which dependencies were incompatible through #conflict # and #conflicting_dependencies class Gem::DependencyResolutionError < Gem::DependencyError attr_reader :conflict def initialize(conflict) @conflict = conflict a, b = conflicting_dependencies super "conflicting dependencies #{a} and #{b}\n#{@conflict.explanation}" end def conflicting_dependencies @conflict.conflicting_dependencies end end ## # Raised when attempting to uninstall a gem that isn't in GEM_HOME. class Gem::GemNotInHomeException < Gem::Exception attr_accessor :spec end ### # Raised when removing a gem with the uninstall command fails class Gem::UninstallError < Gem::Exception attr_accessor :spec end class Gem::DocumentError < Gem::Exception; end ## # Potentially raised when a specification is validated. class Gem::EndOfYAMLException < Gem::Exception; end ## # Signals that a file permission error is preventing the user from # operating on the given directory. class Gem::FilePermissionError < Gem::Exception attr_reader :directory def initialize(directory) @directory = directory super "You don't have write permissions for the #{directory} directory." end end ## # Used to raise parsing and loading errors class Gem::FormatException < Gem::Exception attr_accessor :file_path end class Gem::GemNotFoundException < Gem::Exception; end ## # Raised by the DependencyInstaller when a specific gem cannot be found class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException ## # Creates a new SpecificGemNotFoundException for a gem with the given +name+ # and +version+. Any +errors+ encountered when attempting to find the gem # are also stored. def initialize(name, version, errors=nil) super "Could not find a valid gem '#{name}' (#{version}) locally or in a repository" @name = name @version = version @errors = errors end ## # The name of the gem that could not be found. attr_reader :name ## # The version of the gem that could not be found. attr_reader :version ## # Errors encountered attempting to find the gem. attr_reader :errors end ## # Raised by Gem::Resolver when dependencies conflict and create the # inability to find a valid possible spec for a request. class Gem::ImpossibleDependenciesError < Gem::Exception attr_reader :conflicts attr_reader :request def initialize(request, conflicts) @request = request @conflicts = conflicts super build_message end def build_message # :nodoc: requester = @request.requester requester = requester ? requester.spec.full_name : 'The user' dependency = @request.dependency message = "#{requester} requires #{dependency} but it conflicted:\n".dup @conflicts.each do |_, conflict| message << conflict.explanation end message end def dependency @request.dependency end end class Gem::InstallError < Gem::Exception; end class Gem::RuntimeRequirementNotMetError < Gem::InstallError attr_accessor :suggestion def message [suggestion, super].compact.join("\n\t") end end ## # Potentially raised when a specification is validated. class Gem::InvalidSpecificationException < Gem::Exception; end class Gem::OperationNotSupportedError < Gem::Exception; end ## # Signals that a remote operation cannot be conducted, probably due to not # being connected (or just not finding host). #-- # TODO: create a method that tests connection to the preferred gems server. # All code dealing with remote operations will want this. Failure in that # method should raise this error. class Gem::RemoteError < Gem::Exception; end class Gem::RemoteInstallationCancelled < Gem::Exception; end class Gem::RemoteInstallationSkipped < Gem::Exception; end ## # Represents an error communicating via HTTP. class Gem::RemoteSourceException < Gem::Exception; end ## # Raised when a gem dependencies file specifies a ruby version that does not # match the current version. class Gem::RubyVersionMismatch < Gem::Exception; end ## # Raised by Gem::Validator when something is not right in a gem. class Gem::VerificationError < Gem::Exception; end ## # Raised to indicate that a system exit should occur with the specified # exit_code class Gem::SystemExitException < SystemExit ## # The exit code for the process attr_accessor :exit_code ## # Creates a new SystemExitException with the given +exit_code+ def initialize(exit_code) @exit_code = exit_code super "Exiting RubyGems with exit_code #{exit_code}" end end ## # Raised by Resolver when a dependency requests a gem for which # there is no spec. class Gem::UnsatisfiableDependencyError < Gem::DependencyError ## # The unsatisfiable dependency. This is a # Gem::Resolver::DependencyRequest, not a Gem::Dependency attr_reader :dependency ## # Errors encountered which may have contributed to this exception attr_accessor :errors ## # Creates a new UnsatisfiableDependencyError for the unsatisfiable # Gem::Resolver::DependencyRequest +dep+ def initialize(dep, platform_mismatch=nil) if platform_mismatch and !platform_mismatch.empty? plats = platform_mismatch.map {|x| x.platform.to_s }.sort.uniq super "Unable to resolve dependency: No match for '#{dep}' on this platform. Found: #{plats.join(', ')}" else if dep.explicit? super "Unable to resolve dependency: user requested '#{dep}'" else super "Unable to resolve dependency: '#{dep.request_context}' requires '#{dep}'" end end @dependency = dep @errors = [] end ## # The name of the unresolved dependency def name @dependency.name end ## # The Requirement of the unresolved dependency (not Version). def version @dependency.requirement end end ## # Backwards compatible typo'd exception class for early RubyGems 2.0.x Gem::UnsatisfiableDepedencyError = Gem::UnsatisfiableDependencyError # :nodoc: Gem.deprecate_constant :UnsatisfiableDepedencyError rubygems/rubygems/spec_fetcher.rb 0000644 00000015051 15102661023 0013213 0 ustar 00 # frozen_string_literal: true require_relative 'remote_fetcher' require_relative 'user_interaction' require_relative 'errors' require_relative 'text' require_relative 'name_tuple' ## # SpecFetcher handles metadata updates from remote gem repositories. class Gem::SpecFetcher include Gem::UserInteraction include Gem::Text ## # Cache of latest specs attr_reader :latest_specs # :nodoc: ## # Sources for this SpecFetcher attr_reader :sources # :nodoc: ## # Cache of all released specs attr_reader :specs # :nodoc: ## # Cache of prerelease specs attr_reader :prerelease_specs # :nodoc: @fetcher = nil ## # Default fetcher instance. Use this instead of ::new to reduce object # allocation. def self.fetcher @fetcher ||= new end def self.fetcher=(fetcher) # :nodoc: @fetcher = fetcher end ## # Creates a new SpecFetcher. Ordinarily you want to use the default fetcher # from Gem::SpecFetcher::fetcher which uses the Gem.sources. # # If you need to retrieve specifications from a different +source+, you can # send it as an argument. def initialize(sources = nil) @sources = sources || Gem.sources @update_cache = begin File.stat(Gem.user_home).uid == Process.uid rescue Errno::EACCES, Errno::ENOENT false end @specs = {} @latest_specs = {} @prerelease_specs = {} @caches = { :latest => @latest_specs, :prerelease => @prerelease_specs, :released => @specs, } @fetcher = Gem::RemoteFetcher.fetcher end ## # # Find and fetch gem name tuples that match +dependency+. # # If +matching_platform+ is false, gems for all platforms are returned. def search_for_dependency(dependency, matching_platform=true) found = {} rejected_specs = {} list, errors = available_specs(dependency.identity) list.each do |source, specs| if dependency.name.is_a?(String) && specs.respond_to?(:bsearch) start_index = (0 ... specs.length).bsearch{|i| specs[i].name >= dependency.name } end_index = (0 ... specs.length).bsearch{|i| specs[i].name > dependency.name } specs = specs[start_index ... end_index] if start_index && end_index end found[source] = specs.select do |tup| if dependency.match?(tup) if matching_platform and !Gem::Platform.match_gem?(tup.platform, tup.name) pm = ( rejected_specs[dependency] ||= \ Gem::PlatformMismatch.new(tup.name, tup.version)) pm.add_platform tup.platform false else true end end end end errors += rejected_specs.values tuples = [] found.each do |source, specs| specs.each do |s| tuples << [s, source] end end tuples = tuples.sort_by {|x| x[0] } return [tuples, errors] end ## # Return all gem name tuples who's names match +obj+ def detect(type=:complete) tuples = [] list, _ = available_specs(type) list.each do |source, specs| specs.each do |tup| if yield(tup) tuples << [tup, source] end end end tuples end ## # Find and fetch specs that match +dependency+. # # If +matching_platform+ is false, gems for all platforms are returned. def spec_for_dependency(dependency, matching_platform=true) tuples, errors = search_for_dependency(dependency, matching_platform) specs = [] tuples.each do |tup, source| begin spec = source.fetch_spec(tup) rescue Gem::RemoteFetcher::FetchError => e errors << Gem::SourceFetchProblem.new(source, e) else specs << [spec, source] end end return [specs, errors] end ## # Suggests gems based on the supplied +gem_name+. Returns an array of # alternative gem names. def suggest_gems_from_name(gem_name, type = :latest, num_results = 5) gem_name = gem_name.downcase.tr('_-', '') max = gem_name.size / 2 names = available_specs(type).first.values.flatten(1) matches = names.map do |n| next unless n.match_platform? [n.name, 0] if n.name.downcase.tr('_-', '').include?(gem_name) end.compact if matches.length < num_results matches += names.map do |n| next unless n.match_platform? distance = levenshtein_distance gem_name, n.name.downcase.tr('_-', '') next if distance >= max return [n.name] if distance == 0 [n.name, distance] end.compact end matches = if matches.empty? && type != :prerelease suggest_gems_from_name gem_name, :prerelease else matches.uniq.sort_by {|name, dist| dist } end matches.map {|name, dist| name }.uniq.first(num_results) end ## # Returns a list of gems available for each source in Gem::sources. # # +type+ can be one of 3 values: # :released => Return the list of all released specs # :complete => Return the list of all specs # :latest => Return the list of only the highest version of each gem # :prerelease => Return the list of all prerelease only specs # def available_specs(type) errors = [] list = {} @sources.each_source do |source| begin names = case type when :latest tuples_for source, :latest when :released tuples_for source, :released when :complete names = tuples_for(source, :prerelease, true) + tuples_for(source, :released) names.sort when :abs_latest names = tuples_for(source, :prerelease, true) + tuples_for(source, :latest) names.sort when :prerelease tuples_for(source, :prerelease) else raise Gem::Exception, "Unknown type - :#{type}" end rescue Gem::RemoteFetcher::FetchError => e errors << Gem::SourceFetchProblem.new(source, e) else list[source] = names end end [list, errors] end ## # Retrieves NameTuples from +source+ of the given +type+ (:prerelease, # etc.). If +gracefully_ignore+ is true, errors are ignored. def tuples_for(source, type, gracefully_ignore=false) # :nodoc: @caches[type][source.uri] ||= source.load_specs(type).sort_by {|tup| tup.name } rescue Gem::RemoteFetcher::FetchError raise unless gracefully_ignore [] end end rubygems/rubygems/gem_runner.rb 0000644 00000003633 15102661023 0012725 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative '../rubygems' require_relative 'command_manager' require_relative 'deprecate' ## # Load additional plugins from $LOAD_PATH Gem.load_env_plugins rescue nil ## # Run an instance of the gem program. # # Gem::GemRunner is only intended for internal use by RubyGems itself. It # does not form any public API and may change at any time for any reason. # # If you would like to duplicate functionality of `gem` commands, use the # classes they call directly. class Gem::GemRunner def initialize @command_manager_class = Gem::CommandManager @config_file_class = Gem::ConfigFile end ## # Run the gem command with the following arguments. def run(args) build_args = extract_build_args args do_configuration args cmd = @command_manager_class.instance cmd.command_names.each do |command_name| config_args = Gem.configuration[command_name] config_args = case config_args when String config_args.split ' ' else Array(config_args) end Gem::Command.add_specific_extra_args command_name, config_args end cmd.run Gem.configuration.args, build_args end ## # Separates the build arguments (those following <code>--</code>) from the # other arguments in the list. def extract_build_args(args) # :nodoc: return [] unless offset = args.index('--') build_args = args.slice!(offset...args.length) build_args.shift build_args end private def do_configuration(args) Gem.configuration = @config_file_class.new(args) Gem.use_paths Gem.configuration[:gemhome], Gem.configuration[:gempath] Gem::Command.extra_args = Gem.configuration[:gem] end end Gem.load_plugins rubygems/rubygems/specification_policy.rb 0000644 00000032175 15102661023 0014766 0 ustar 00 require_relative 'user_interaction' class Gem::SpecificationPolicy include Gem::UserInteraction VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc: SPECIAL_CHARACTERS = /\A[#{Regexp.escape('.-_')}]+/.freeze # :nodoc: VALID_URI_PATTERN = %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}.freeze # :nodoc: METADATA_LINK_KEYS = %w[ bug_tracker_uri changelog_uri documentation_uri homepage_uri mailing_list_uri source_code_uri wiki_uri funding_uri ].freeze # :nodoc: def initialize(specification) @warnings = 0 @specification = specification end ## # If set to true, run packaging-specific checks, as well. attr_accessor :packaging ## # Does a sanity check on the specification. # # Raises InvalidSpecificationException if the spec does not pass the # checks. # # It also performs some validations that do not raise but print warning # messages instead. def validate(strict = false) validate_required! validate_optional(strict) if packaging || strict true end ## # Does a sanity check on the specification. # # Raises InvalidSpecificationException if the spec does not pass the # checks. # # Only runs checks that are considered necessary for the specification to be # functional. def validate_required! validate_nil_attributes validate_rubygems_version validate_required_attributes validate_name validate_require_paths @specification.keep_only_files_and_directories validate_non_files validate_self_inclusion_in_files_list validate_specification_version validate_platform validate_array_attributes validate_authors_field validate_metadata validate_licenses_length validate_lazy_metadata validate_duplicate_dependencies end def validate_optional(strict) validate_licenses validate_permissions validate_values validate_dependencies validate_extensions validate_removed_attributes if @warnings > 0 if strict error "specification has warnings" else alert_warning help_text end end end ## # Implementation for Specification#validate_metadata def validate_metadata metadata = @specification.metadata unless Hash === metadata error 'metadata must be a hash' end metadata.each do |key, value| entry = "metadata['#{key}']" if !key.kind_of?(String) error "metadata keys must be a String" end if key.size > 128 error "metadata key is too large (#{key.size} > 128)" end if !value.kind_of?(String) error "#{entry} value must be a String" end if value.size > 1024 error "#{entry} value is too large (#{value.size} > 1024)" end if METADATA_LINK_KEYS.include? key if value !~ VALID_URI_PATTERN error "#{entry} has invalid link: #{value.inspect}" end end end end ## # Checks that no duplicate dependencies are specified. def validate_duplicate_dependencies # :nodoc: # NOTE: see REFACTOR note in Gem::Dependency about types - this might be brittle seen = Gem::Dependency::TYPES.inject({}) {|types, type| types.merge({ type => {}}) } error_messages = [] @specification.dependencies.each do |dep| if prev = seen[dep.type][dep.name] error_messages << <<-MESSAGE duplicate dependency on #{dep}, (#{prev.requirement}) use: add_#{dep.type}_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}' MESSAGE end seen[dep.type][dep.name] = dep end if error_messages.any? error error_messages.join end end ## # Checks that dependencies use requirements as we recommend. Warnings are # issued when dependencies are open-ended or overly strict for semantic # versioning. def validate_dependencies # :nodoc: warning_messages = [] @specification.dependencies.each do |dep| prerelease_dep = dep.requirements_list.any? do |req| Gem::Requirement.new(req).prerelease? end warning_messages << "prerelease dependency on #{dep} is not recommended" if prerelease_dep && !@specification.version.prerelease? open_ended = dep.requirement.requirements.all? do |op, version| not version.prerelease? and (op == '>' or op == '>=') end if open_ended op, dep_version = dep.requirement.requirements.first segments = dep_version.segments base = segments.first 2 recommendation = if (op == '>' || op == '>=') && segments == [0] " use a bounded requirement, such as '~> x.y'" else bugfix = if op == '>' ", '> #{dep_version}'" elsif op == '>=' and base != segments ", '>= #{dep_version}'" end " if #{dep.name} is semantically versioned, use:\n" \ " add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}" end warning_messages << ["open-ended dependency on #{dep} is not recommended", recommendation].join("\n") + "\n" end end if warning_messages.any? warning_messages.each {|warning_message| warning warning_message } end end ## # Issues a warning for each file to be packaged which is world-readable. # # Implementation for Specification#validate_permissions def validate_permissions return if Gem.win_platform? @specification.files.each do |file| next unless File.file?(file) next if File.stat(file).mode & 0444 == 0444 warning "#{file} is not world-readable" end @specification.executables.each do |name| exec = File.join @specification.bindir, name next unless File.file?(exec) next if File.stat(exec).executable? warning "#{exec} is not executable" end end private def validate_nil_attributes nil_attributes = Gem::Specification.non_nil_attributes.select do |attrname| @specification.instance_variable_get("@#{attrname}").nil? end return if nil_attributes.empty? error "#{nil_attributes.join ', '} must not be nil" end def validate_rubygems_version return unless packaging rubygems_version = @specification.rubygems_version return if rubygems_version == Gem::VERSION error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}" end def validate_required_attributes Gem::Specification.required_attributes.each do |symbol| unless @specification.send symbol error "missing value for attribute #{symbol}" end end end def validate_name name = @specification.name if !name.is_a?(String) error "invalid value for attribute name: \"#{name.inspect}\" must be a string" elsif name !~ /[a-zA-Z]/ error "invalid value for attribute name: #{name.dump} must include at least one letter" elsif name !~ VALID_NAME_PATTERN error "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores" elsif name =~ SPECIAL_CHARACTERS error "invalid value for attribute name: #{name.dump} can not begin with a period, dash, or underscore" end end def validate_require_paths return unless @specification.raw_require_paths.empty? error 'specification must have at least one require_path' end def validate_non_files return unless packaging non_files = @specification.files.reject {|x| File.file?(x) || File.symlink?(x) } unless non_files.empty? error "[\"#{non_files.join "\", \""}\"] are not files" end end def validate_self_inclusion_in_files_list file_name = @specification.file_name return unless @specification.files.include?(file_name) error "#{@specification.full_name} contains itself (#{file_name}), check your files list" end def validate_specification_version return if @specification.specification_version.is_a?(Integer) error 'specification_version must be an Integer (did you mean version?)' end def validate_platform platform = @specification.platform case platform when Gem::Platform, Gem::Platform::RUBY # ok else error "invalid platform #{platform.inspect}, see Gem::Platform" end end def validate_array_attributes Gem::Specification.array_attributes.each do |field| validate_array_attribute(field) end end def validate_array_attribute(field) val = @specification.send(field) klass = case field when :dependencies then Gem::Dependency else String end unless Array === val and val.all? {|x| x.kind_of?(klass) } error "#{field} must be an Array of #{klass}" end end def validate_authors_field return unless @specification.authors.empty? error "authors may not be empty" end def validate_licenses_length licenses = @specification.licenses licenses.each do |license| if license.length > 64 error "each license must be 64 characters or less" end end end def validate_licenses licenses = @specification.licenses licenses.each do |license| if !Gem::Licenses.match?(license) suggestions = Gem::Licenses.suggestions(license) message = <<-WARNING license value '#{license}' is invalid. Use a license identifier from http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license. WARNING message += "Did you mean #{suggestions.map {|s| "'#{s}'" }.join(', ')}?\n" unless suggestions.nil? warning(message) end end warning <<-WARNING if licenses.empty? licenses is empty, but is recommended. Use a license identifier from http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license. WARNING end LAZY = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '') LAZY_PATTERN = /\AFI XME|\ATO DO/x.freeze HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i.freeze def validate_lazy_metadata unless @specification.authors.grep(LAZY_PATTERN).empty? error "#{LAZY} is not an author" end unless Array(@specification.email).grep(LAZY_PATTERN).empty? error "#{LAZY} is not an email" end if @specification.description =~ LAZY_PATTERN error "#{LAZY} is not a description" end if @specification.summary =~ LAZY_PATTERN error "#{LAZY} is not a summary" end homepage = @specification.homepage # Make sure a homepage is valid HTTP/HTTPS URI if homepage and not homepage.empty? require 'uri' begin homepage_uri = URI.parse(homepage) unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class error "\"#{homepage}\" is not a valid HTTP URI" end rescue URI::InvalidURIError error "\"#{homepage}\" is not a valid HTTP URI" end end end def validate_values %w[author homepage summary files].each do |attribute| validate_attribute_present(attribute) end if @specification.description == @specification.summary warning "description and summary are identical" end # TODO: raise at some given date warning "deprecated autorequire specified" if @specification.autorequire @specification.executables.each do |executable| validate_shebang_line_in(executable) end @specification.files.select {|f| File.symlink?(f) }.each do |file| warning "#{file} is a symlink, which is not supported on all platforms" end end def validate_attribute_present(attribute) value = @specification.send attribute warning("no #{attribute} specified") if value.nil? || value.empty? end def validate_shebang_line_in(executable) executable_path = File.join(@specification.bindir, executable) return if File.read(executable_path, 2) == '#!' warning "#{executable_path} is missing #! line" end def validate_removed_attributes # :nodoc: @specification.removed_method_calls.each do |attr| warning("#{attr} is deprecated and ignored. Please remove this from your gemspec to ensure that your gem continues to build in the future.") end end def validate_extensions # :nodoc: require_relative 'ext' builder = Gem::Ext::Builder.new(@specification) rake_extension = @specification.extensions.any? {|s| builder.builder_for(s) == Gem::Ext::RakeBuilder } rake_dependency = @specification.dependencies.any? {|d| d.name == 'rake' } warning <<-WARNING if rake_extension && !rake_dependency You have specified rake based extension, but rake is not added as dependency. It is recommended to add rake as a dependency in gemspec since there's no guarantee rake will be already installed. WARNING end def warning(statement) # :nodoc: @warnings += 1 alert_warning statement end def error(statement) # :nodoc: raise Gem::InvalidSpecificationException, statement ensure alert_warning help_text end def help_text # :nodoc: "See https://guides.rubygems.org/specification-reference/ for help" end end rubygems/rubygems/defaults/operating_system.rb 0000644 00000010403 15102661023 0015760 0 ustar 00 module Gem class << self ## # Returns full path of previous but one directory of dir in path # E.g. for '/usr/share/ruby', 'ruby', it returns '/usr' def previous_but_one_dir_to(path, dir) return unless path split_path = path.split(File::SEPARATOR) File.join(split_path.take_while { |one_dir| one_dir !~ /^#{dir}$/ }[0..-2]) end private :previous_but_one_dir_to ## # Detects --install-dir option specified on command line. def opt_install_dir? @opt_install_dir ||= ARGV.include?('--install-dir') || ARGV.include?('-i') end private :opt_install_dir? ## # Detects --build-root option specified on command line. def opt_build_root? @opt_build_root ||= ARGV.include?('--build-root') end private :opt_build_root? ## # Tries to detect, if arguments and environment variables suggest that # 'gem install' is executed from rpmbuild. def rpmbuild? @rpmbuild ||= ENV['RPM_PACKAGE_NAME'] && (opt_install_dir? || opt_build_root?) end private :rpmbuild? ## # Default gems locations allowed on FHS system (/usr, /usr/share). # The locations are derived from directories specified during build # configuration. def default_locations @default_locations ||= { :system => previous_but_one_dir_to(RbConfig::CONFIG['vendordir'], RbConfig::CONFIG['RUBY_INSTALL_NAME']), :local => previous_but_one_dir_to(RbConfig::CONFIG['sitedir'], RbConfig::CONFIG['RUBY_INSTALL_NAME']) } end ## # For each location provides set of directories for binaries (:bin_dir) # platform independent (:gem_dir) and dependent (:ext_dir) files. def default_dirs @libdir ||= case RUBY_PLATFORM when 'java' RbConfig::CONFIG['datadir'] else RbConfig::CONFIG['libdir'] end @default_dirs ||= default_locations.inject(Hash.new) do |hash, location| destination, path = location hash[destination] = if path { :bin_dir => File.join(path, RbConfig::CONFIG['bindir'].split(File::SEPARATOR).last), :gem_dir => File.join(path, RbConfig::CONFIG['datadir'].split(File::SEPARATOR).last, 'gems'), :ext_dir => File.join(path, @libdir.split(File::SEPARATOR).last, 'gems') } else { :bin_dir => '', :gem_dir => '', :ext_dir => '' } end hash end end ## # Remove methods we are going to override. This avoids "method redefined;" # warnings otherwise issued by Ruby. remove_method :operating_system_defaults if method_defined? :operating_system_defaults remove_method :default_dir if method_defined? :default_dir remove_method :default_path if method_defined? :default_path remove_method :default_ext_dir_for if method_defined? :default_ext_dir_for ## # Regular user installs into user directory, root manages /usr/local. def operating_system_defaults unless opt_build_root? options = if Process.uid == 0 "--install-dir=#{Gem.default_dirs[:local][:gem_dir]} --bindir #{Gem.default_dirs[:local][:bin_dir]}" end {"gem" => options} else {} end end ## # RubyGems default overrides. def default_dir Gem.default_dirs[:system][:gem_dir] end def default_path path = default_dirs.collect {|location, paths| paths[:gem_dir]} path.unshift Gem.user_dir if File.exist? Gem.user_home path end def default_ext_dir_for base_dir dir = if rpmbuild? build_dir = base_dir.chomp Gem.default_dirs[:system][:gem_dir] if build_dir != base_dir File.join build_dir, Gem.default_dirs[:system][:ext_dir] end else dirs = Gem.default_dirs.detect {|location, paths| paths[:gem_dir] == base_dir} dirs && dirs.last[:ext_dir] end dir && File.join(dir, RbConfig::CONFIG['RUBY_INSTALL_NAME']) end # This method should be available since RubyGems 2.2 until RubyGems 3.0. # https://github.com/rubygems/rubygems/issues/749 if method_defined? :install_extension_in_lib remove_method :install_extension_in_lib def install_extension_in_lib false end end end end rubygems/rubygems/installer_uninstaller_utils.rb 0000644 00000001405 15102661023 0016414 0 ustar 00 # frozen_string_literal: true ## # Helper methods for both Gem::Installer and Gem::Uninstaller module Gem::InstallerUninstallerUtils def regenerate_plugins_for(spec, plugins_dir) plugins = spec.plugins return if plugins.empty? require 'pathname' spec.plugins.each do |plugin| plugin_script_path = File.join plugins_dir, "#{spec.name}_plugin#{File.extname(plugin)}" File.open plugin_script_path, 'wb' do |file| file.puts "require_relative '#{Pathname.new(plugin).relative_path_from(Pathname.new(plugins_dir))}'" end verbose plugin_script_path end end def remove_plugins_for(spec, plugins_dir) FileUtils.rm_f Gem::Util.glob_files_in_dir("#{spec.name}#{Gem.plugin_suffix_pattern}", plugins_dir) end end rubygems/rubygems/psych_additions.rb 0000644 00000000454 15102661023 0013746 0 ustar 00 # frozen_string_literal: true # This exists just to satisfy bugs in marshal'd gemspecs that # contain a reference to YAML::PrivateType. We prune these out # in Specification._load, but if we don't have the constant, Marshal # blows up. module Psych # :nodoc: class PrivateType # :nodoc: end end rubygems/rubygems/s3_uri_signer.rb 0000644 00000013641 15102661023 0013337 0 ustar 00 require_relative 'openssl' ## # S3URISigner implements AWS SigV4 for S3 Source to avoid a dependency on the aws-sdk-* gems # More on AWS SigV4: https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html class Gem::S3URISigner class ConfigurationError < Gem::Exception def initialize(message) super message end def to_s # :nodoc: "#{super}" end end class InstanceProfileError < Gem::Exception def initialize(message) super message end def to_s # :nodoc: "#{super}" end end attr_accessor :uri def initialize(uri) @uri = uri end ## # Signs S3 URI using query-params according to the reference: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html def sign(expiration = 86400) s3_config = fetch_s3_config current_time = Time.now.utc date_time = current_time.strftime("%Y%m%dT%H%m%SZ") date = date_time[0,8] credential_info = "#{date}/#{s3_config.region}/s3/aws4_request" canonical_host = "#{uri.host}.s3.#{s3_config.region}.amazonaws.com" query_params = generate_canonical_query_params(s3_config, date_time, credential_info, expiration) canonical_request = generate_canonical_request(canonical_host, query_params) string_to_sign = generate_string_to_sign(date_time, credential_info, canonical_request) signature = generate_signature(s3_config, date, string_to_sign) URI.parse("https://#{canonical_host}#{uri.path}?#{query_params}&X-Amz-Signature=#{signature}") end private S3Config = Struct.new :access_key_id, :secret_access_key, :security_token, :region def generate_canonical_query_params(s3_config, date_time, credential_info, expiration) canonical_params = {} canonical_params["X-Amz-Algorithm"] = "AWS4-HMAC-SHA256" canonical_params["X-Amz-Credential"] = "#{s3_config.access_key_id}/#{credential_info}" canonical_params["X-Amz-Date"] = date_time canonical_params["X-Amz-Expires"] = expiration.to_s canonical_params["X-Amz-SignedHeaders"] = "host" canonical_params["X-Amz-Security-Token"] = s3_config.security_token if s3_config.security_token # Sorting is required to generate proper signature canonical_params.sort.to_h.map do |key, value| "#{base64_uri_escape(key)}=#{base64_uri_escape(value)}" end.join("&") end def generate_canonical_request(canonical_host, query_params) [ "GET", uri.path, query_params, "host:#{canonical_host}", "", # empty params "host", "UNSIGNED-PAYLOAD", ].join("\n") end def generate_string_to_sign(date_time, credential_info, canonical_request) [ "AWS4-HMAC-SHA256", date_time, credential_info, OpenSSL::Digest::SHA256.hexdigest(canonical_request), ].join("\n") end def generate_signature(s3_config, date, string_to_sign) date_key = OpenSSL::HMAC.digest("sha256", "AWS4" + s3_config.secret_access_key, date) date_region_key = OpenSSL::HMAC.digest("sha256", date_key, s3_config.region) date_region_service_key = OpenSSL::HMAC.digest("sha256", date_region_key, "s3") signing_key = OpenSSL::HMAC.digest("sha256", date_region_service_key, "aws4_request") OpenSSL::HMAC.hexdigest("sha256", signing_key, string_to_sign) end ## # Extracts S3 configuration for S3 bucket def fetch_s3_config return S3Config.new(uri.user, uri.password, nil, "us-east-1") if uri.user && uri.password s3_source = Gem.configuration[:s3_source] || Gem.configuration["s3_source"] host = uri.host raise ConfigurationError.new("no s3_source key exists in .gemrc") unless s3_source auth = s3_source[host] || s3_source[host.to_sym] raise ConfigurationError.new("no key for host #{host} in s3_source in .gemrc") unless auth provider = auth[:provider] || auth["provider"] case provider when "env" id = ENV["AWS_ACCESS_KEY_ID"] secret = ENV["AWS_SECRET_ACCESS_KEY"] security_token = ENV["AWS_SESSION_TOKEN"] when "instance_profile" credentials = ec2_metadata_credentials_json id = credentials["AccessKeyId"] secret = credentials["SecretAccessKey"] security_token = credentials["Token"] else id = auth[:id] || auth["id"] secret = auth[:secret] || auth["secret"] security_token = auth[:security_token] || auth["security_token"] end raise ConfigurationError.new("s3_source for #{host} missing id or secret") unless id && secret region = auth[:region] || auth["region"] || "us-east-1" S3Config.new(id, secret, security_token, region) end def base64_uri_escape(str) str.gsub(/[\+\/=\n]/, BASE64_URI_TRANSLATE) end def ec2_metadata_credentials_json require 'net/http' require_relative 'request' require_relative 'request/connection_pools' require 'json' iam_info = ec2_metadata_request(EC2_IAM_INFO) # Expected format: arn:aws:iam::<id>:instance-profile/<role_name> role_name = iam_info['InstanceProfileArn'].split('/').last ec2_metadata_request(EC2_IAM_SECURITY_CREDENTIALS + role_name) end def ec2_metadata_request(url) uri = URI(url) @request_pool ||= create_request_pool(uri) request = Gem::Request.new(uri, Net::HTTP::Get, nil, @request_pool) response = request.fetch case response when Net::HTTPOK then JSON.parse(response.body) else raise InstanceProfileError.new("Unable to fetch AWS metadata from #{uri}: #{response.message} #{response.code}") end end def create_request_pool(uri) proxy_uri = Gem::Request.proxy_uri(Gem::Request.get_proxy_from_env(uri.scheme)) certs = Gem::Request.get_cert_files Gem::Request::ConnectionPools.new(proxy_uri, certs).pool_for(uri) end BASE64_URI_TRANSLATE = { "+" => "%2B", "/" => "%2F", "=" => "%3D", "\n" => "" }.freeze EC2_IAM_INFO = "http://169.254.169.254/latest/meta-data/iam/info".freeze EC2_IAM_SECURITY_CREDENTIALS = "http://169.254.169.254/latest/meta-data/iam/security-credentials/".freeze end rubygems/rubygems/platform.rb 0000644 00000015236 15102661023 0012412 0 ustar 00 # frozen_string_literal: true require_relative "deprecate" ## # Available list of platforms for targeting Gem installations. # # See `gem help platform` for information on platform matching. class Gem::Platform @local = nil attr_accessor :cpu, :os, :version def self.local arch = RbConfig::CONFIG['arch'] arch = "#{arch}_60" if arch =~ /mswin(?:32|64)$/ @local ||= new(arch) end def self.match(platform) match_platforms?(platform, Gem.platforms) end def self.match_platforms?(platform, platforms) platforms.any? do |local_platform| platform.nil? or local_platform == platform or (local_platform != Gem::Platform::RUBY and local_platform =~ platform) end end private_class_method :match_platforms? def self.match_spec?(spec) match_gem?(spec.platform, spec.name) end def self.match_gem?(platform, gem_name) # Note: this method might be redefined by Ruby implementations to # customize behavior per RUBY_ENGINE, gem_name or other criteria. match_platforms?(platform, Gem.platforms) end def self.installable?(spec) if spec.respond_to? :installable_platform? spec.installable_platform? else match_spec? spec end end def self.new(arch) # :nodoc: case arch when Gem::Platform::CURRENT then Gem::Platform.local when Gem::Platform::RUBY, nil, '' then Gem::Platform::RUBY else super end end def initialize(arch) case arch when Array then @cpu, @os, @version = arch when String then arch = arch.split '-' if arch.length > 2 and arch.last !~ /\d/ # reassemble x86-linux-gnu extra = arch.pop arch.last << "-#{extra}" end cpu = arch.shift @cpu = case cpu when /i\d86/ then 'x86' else cpu end if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ # for command-line @os, @version = arch return end os, = arch @cpu, os = nil, cpu if os.nil? # legacy jruby @os, @version = case os when /aix(\d+)?/ then [ 'aix', $1 ] when /cygwin/ then [ 'cygwin', nil ] when /darwin(\d+)?/ then [ 'darwin', $1 ] when /^macruby$/ then [ 'macruby', nil ] when /freebsd(\d+)?/ then [ 'freebsd', $1 ] when /hpux(\d+)?/ then [ 'hpux', $1 ] when /^java$/, /^jruby$/ then [ 'java', nil ] when /^java([\d.]*)/ then [ 'java', $1 ] when /^dalvik(\d+)?$/ then [ 'dalvik', $1 ] when /^dotnet$/ then [ 'dotnet', nil ] when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ] when /linux-?((?!gnu)\w+)?/ then [ 'linux', $1 ] when /mingw32/ then [ 'mingw32', nil ] when /mingw-?(\w+)?/ then [ 'mingw', $1 ] when /(mswin\d+)(\_(\d+))?/ then os, version = $1, $3 @cpu = 'x86' if @cpu.nil? and os =~ /32$/ [os, version] when /netbsdelf/ then [ 'netbsdelf', nil ] when /openbsd(\d+\.\d+)?/ then [ 'openbsd', $1 ] when /bitrig(\d+\.\d+)?/ then [ 'bitrig', $1 ] when /solaris(\d+\.\d+)?/ then [ 'solaris', $1 ] # test when /^(\w+_platform)(\d+)?/ then [ $1, $2 ] else [ 'unknown', nil ] end when Gem::Platform then @cpu = arch.cpu @os = arch.os @version = arch.version else raise ArgumentError, "invalid argument #{arch.inspect}" end end def to_a [@cpu, @os, @version] end def to_s to_a.compact.join '-' end ## # Is +other+ equal to this platform? Two platforms are equal if they have # the same CPU, OS and version. def ==(other) self.class === other and to_a == other.to_a end alias :eql? :== def hash # :nodoc: to_a.hash end ## # Does +other+ match this platform? Two platforms match if they have the # same CPU, or either has a CPU of 'universal', they have the same OS, and # they have the same version, or either has no version. # # Additionally, the platform will match if the local CPU is 'arm' and the # other CPU starts with "arm" (for generic ARM family support). def ===(other) return nil unless Gem::Platform === other # cpu ([nil,'universal'].include?(@cpu) or [nil, 'universal'].include?(other.cpu) or @cpu == other.cpu or (@cpu == 'arm' and other.cpu.start_with?("arm"))) and # os @os == other.os and # version (@version.nil? or other.version.nil? or @version == other.version) end ## # Does +other+ match this platform? If +other+ is a String it will be # converted to a Gem::Platform first. See #=== for matching rules. def =~(other) case other when Gem::Platform then # nop when String then # This data is from http://gems.rubyforge.org/gems/yaml on 19 Aug 2007 other = case other when /^i686-darwin(\d)/ then ['x86', 'darwin', $1 ] when /^i\d86-linux/ then ['x86', 'linux', nil ] when 'java', 'jruby' then [nil, 'java', nil ] when /^dalvik(\d+)?$/ then [nil, 'dalvik', $1 ] when /dotnet(\-(\d+\.\d+))?/ then ['universal','dotnet', $2 ] when /mswin32(\_(\d+))?/ then ['x86', 'mswin32', $2 ] when /mswin64(\_(\d+))?/ then ['x64', 'mswin64', $2 ] when 'powerpc-darwin' then ['powerpc', 'darwin', nil ] when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1 ] when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8' ] when /universal-darwin(\d)/ then ['universal', 'darwin', $1 ] else other end other = Gem::Platform.new other else return nil end self === other end ## # A pure-Ruby gem that may use Gem::Specification#extensions to build # binary files. RUBY = 'ruby'.freeze ## # A platform-specific gem that is built for the packaging Ruby's platform. # This will be replaced with Gem::Platform::local. CURRENT = 'current'.freeze end rubygems/rubygems/path_support.rb 0000644 00000003605 15102661023 0013313 0 ustar 00 # frozen_string_literal: true ## # # Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings # to the rest of RubyGems. # class Gem::PathSupport ## # The default system path for managing Gems. attr_reader :home ## # Array of paths to search for Gems. attr_reader :path ## # Directory with spec cache attr_reader :spec_cache_dir # :nodoc: ## # # Constructor. Takes a single argument which is to be treated like a # hashtable, or defaults to ENV, the system environment. # def initialize(env) @home = env["GEM_HOME"] || Gem.default_dir if File::ALT_SEPARATOR @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR) end @home = expand(@home) @path = split_gem_path env["GEM_PATH"], @home @spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir @spec_cache_dir = @spec_cache_dir.dup.tap(&Gem::UNTAINT) end private ## # Split the Gem search path (as reported by Gem.path). def split_gem_path(gpaths, home) # FIX: it should be [home, *path], not [*path, home] gem_path = [] if gpaths gem_path = gpaths.split(Gem.path_separator) # Handle the path_separator being set to a regexp, which will cause # end_with? to error if gpaths =~ /#{Gem.path_separator}\z/ gem_path += default_path end if File::ALT_SEPARATOR gem_path.map! do |this_path| this_path.gsub File::ALT_SEPARATOR, File::SEPARATOR end end gem_path << home else gem_path = default_path end gem_path.map {|path| expand(path) }.uniq end # Return the default Gem path def default_path gem_path = Gem.default_path + [@home] if defined?(APPLE_GEM_HOME) gem_path << APPLE_GEM_HOME end gem_path end def expand(path) if File.directory?(path) File.realpath(path) else path end end end rubygems/rubygems/openssl.rb 0000644 00000000175 15102661023 0012245 0 ustar 00 # frozen_string_literal: true autoload :OpenSSL, "openssl" module Gem HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc: end rubygems/rubygems/security_option.rb 0000644 00000002073 15102661023 0014020 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative '../rubygems' # forward-declare module Gem::Security # :nodoc: class Policy # :nodoc: end end ## # Mixin methods for security option for Gem::Commands module Gem::SecurityOption def add_security_option Gem::OptionParser.accept Gem::Security::Policy do |value| require_relative 'security' raise Gem::OptionParser::InvalidArgument, 'OpenSSL not installed' unless defined?(Gem::Security::HighSecurity) policy = Gem::Security::Policies[value] unless policy valid = Gem::Security::Policies.keys.sort raise Gem::OptionParser::InvalidArgument, "#{value} (#{valid.join ', '} are valid)" end policy end add_option(:"Install/Update", '-P', '--trust-policy POLICY', Gem::Security::Policy, 'Specify gem trust policy') do |value, options| options[:security_policy] = value end end end rubygems/rubygems/version_option.rb 0000644 00000004325 15102661023 0013640 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative '../rubygems' ## # Mixin methods for --version and --platform Gem::Command options. module Gem::VersionOption ## # Add the --platform option to the option parser. def add_platform_option(task = command, *wrap) Gem::OptionParser.accept Gem::Platform do |value| if value == Gem::Platform::RUBY value else Gem::Platform.new value end end add_option('--platform PLATFORM', Gem::Platform, "Specify the platform of gem to #{task}", *wrap) do |value, options| unless options[:added_platform] Gem.platforms = [Gem::Platform::RUBY] options[:added_platform] = true end Gem.platforms << value unless Gem.platforms.include? value end end ## # Add the --prerelease option to the option parser. def add_prerelease_option(*wrap) add_option("--[no-]prerelease", "Allow prerelease versions of a gem", *wrap) do |value, options| options[:prerelease] = value options[:explicit_prerelease] = true end end ## # Add the --version option to the option parser. def add_version_option(task = command, *wrap) Gem::OptionParser.accept Gem::Requirement do |value| Gem::Requirement.new(*value.split(/\s*,\s*/)) end add_option('-v', '--version VERSION', Gem::Requirement, "Specify version of gem to #{task}", *wrap) do |value, options| # Allow handling for multiple --version operators if options[:version] && !options[:version].none? options[:version].concat([value]) else options[:version] = value end explicit_prerelease_set = !options[:explicit_prerelease].nil? options[:explicit_prerelease] = false unless explicit_prerelease_set options[:prerelease] = value.prerelease? unless options[:explicit_prerelease] end end ## # Extract platform given on the command line def get_platform_from_requirements(requirements) Gem.platforms[1].to_s if requirements.key? :added_platform end end rubygems/rubygems/package/io_source.rb 0000644 00000001455 15102661023 0014146 0 ustar 00 # frozen_string_literal: true ## # Supports reading and writing gems from/to a generic IO object. This is # useful for other applications built on top of rubygems, such as # rubygems.org. # # This is a private class, do not depend on it directly. Instead, pass an IO # object to `Gem::Package.new`. class Gem::Package::IOSource < Gem::Package::Source # :nodoc: all attr_reader :io def initialize(io) @io = io end def start @start ||= begin if io.pos > 0 raise Gem::Package::Error, "Cannot read start unless IO is at start" end value = io.read 20 io.rewind value end end def present? true end def with_read_io yield io ensure io.rewind end def with_write_io yield io ensure io.rewind end def path end end rubygems/rubygems/package/tar_reader/entry.rb 0000644 00000005024 15102661023 0015424 0 ustar 00 # frozen_string_literal: true #++ # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. #-- ## # Class for reading entries out of a tar file class Gem::Package::TarReader::Entry ## # Header for this tar entry attr_reader :header ## # Creates a new tar entry for +header+ that will be read from +io+ def initialize(header, io) @closed = false @header = header @io = io @orig_pos = @io.pos @read = 0 end def check_closed # :nodoc: raise IOError, "closed #{self.class}" if closed? end ## # Number of bytes read out of the tar entry def bytes_read @read end ## # Closes the tar entry def close @closed = true end ## # Is the tar entry closed? def closed? @closed end ## # Are we at the end of the tar entry? def eof? check_closed @read >= @header.size end ## # Full name of the tar entry def full_name if @header.prefix != "" File.join @header.prefix, @header.name else @header.name end rescue ArgumentError => e raise unless e.message == 'string contains null byte' raise Gem::Package::TarInvalidError, 'tar is corrupt, name contains null byte' end ## # Read one byte from the tar entry def getc check_closed return nil if @read >= @header.size ret = @io.getc @read += 1 if ret ret end ## # Is this tar entry a directory? def directory? @header.typeflag == "5" end ## # Is this tar entry a file? def file? @header.typeflag == "0" end ## # Is this tar entry a symlink? def symlink? @header.typeflag == "2" end ## # The position in the tar entry def pos check_closed bytes_read end def size @header.size end alias length size ## # Reads +len+ bytes from the tar file entry, or the rest of the entry if # nil def read(len = nil) check_closed return nil if @read >= @header.size len ||= @header.size - @read max_read = [len, @header.size - @read].min ret = @io.read max_read @read += ret.size ret end def readpartial(maxlen = nil, outbuf = "".b) check_closed raise EOFError if @read >= @header.size maxlen ||= @header.size - @read max_read = [maxlen, @header.size - @read].min @io.readpartial(max_read, outbuf) @read += outbuf.size outbuf end ## # Rewinds to the beginning of the tar file entry def rewind check_closed @io.pos = @orig_pos @read = 0 end end rubygems/rubygems/package/source.rb 0000644 00000000107 15102661023 0013450 0 ustar 00 # frozen_string_literal: true class Gem::Package::Source # :nodoc: end rubygems/rubygems/package/tar_header.rb 0000644 00000013630 15102661023 0014253 0 ustar 00 # frozen_string_literal: true #-- # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. #++ ## #-- # struct tarfile_entry_posix { # char name[100]; # ASCII + (Z unless filled) # char mode[8]; # 0 padded, octal, null # char uid[8]; # ditto # char gid[8]; # ditto # char size[12]; # 0 padded, octal, null # char mtime[12]; # 0 padded, octal, null # char checksum[8]; # 0 padded, octal, null, space # char typeflag[1]; # file: "0" dir: "5" # char linkname[100]; # ASCII + (Z unless filled) # char magic[6]; # "ustar\0" # char version[2]; # "00" # char uname[32]; # ASCIIZ # char gname[32]; # ASCIIZ # char devmajor[8]; # 0 padded, octal, null # char devminor[8]; # o padded, octal, null # char prefix[155]; # ASCII + (Z unless filled) # }; #++ # A header for a tar file class Gem::Package::TarHeader ## # Fields in the tar header FIELDS = [ :checksum, :devmajor, :devminor, :gid, :gname, :linkname, :magic, :mode, :mtime, :name, :prefix, :size, :typeflag, :uid, :uname, :version, ].freeze ## # Pack format for a tar header PACK_FORMAT = 'a100' + # name 'a8' + # mode 'a8' + # uid 'a8' + # gid 'a12' + # size 'a12' + # mtime 'a7a' + # chksum 'a' + # typeflag 'a100' + # linkname 'a6' + # magic 'a2' + # version 'a32' + # uname 'a32' + # gname 'a8' + # devmajor 'a8' + # devminor 'a155' # prefix ## # Unpack format for a tar header UNPACK_FORMAT = 'A100' + # name 'A8' + # mode 'A8' + # uid 'A8' + # gid 'A12' + # size 'A12' + # mtime 'A8' + # checksum 'A' + # typeflag 'A100' + # linkname 'A6' + # magic 'A2' + # version 'A32' + # uname 'A32' + # gname 'A8' + # devmajor 'A8' + # devminor 'A155' # prefix attr_reader(*FIELDS) EMPTY_HEADER = ("\0" * 512).freeze # :nodoc: ## # Creates a tar header from IO +stream+ def self.from(stream) header = stream.read 512 empty = (EMPTY_HEADER == header) fields = header.unpack UNPACK_FORMAT new :name => fields.shift, :mode => strict_oct(fields.shift), :uid => oct_or_256based(fields.shift), :gid => oct_or_256based(fields.shift), :size => strict_oct(fields.shift), :mtime => strict_oct(fields.shift), :checksum => strict_oct(fields.shift), :typeflag => fields.shift, :linkname => fields.shift, :magic => fields.shift, :version => strict_oct(fields.shift), :uname => fields.shift, :gname => fields.shift, :devmajor => strict_oct(fields.shift), :devminor => strict_oct(fields.shift), :prefix => fields.shift, :empty => empty end def self.strict_oct(str) return str.strip.oct if str.strip =~ /\A[0-7]*\z/ raise ArgumentError, "#{str.inspect} is not an octal string" end def self.oct_or_256based(str) # \x80 flags a positive 256-based number # \ff flags a negative 256-based number # In case we have a match, parse it as a signed binary value # in big-endian order, except that the high-order bit is ignored. return str.unpack('N2').last if str =~ /\A[\x80\xff]/n strict_oct(str) end ## # Creates a new TarHeader using +vals+ def initialize(vals) unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] raise ArgumentError, ":name, :size, :prefix and :mode required" end vals[:uid] ||= 0 vals[:gid] ||= 0 vals[:mtime] ||= 0 vals[:checksum] ||= "" vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty? vals[:magic] ||= "ustar" vals[:version] ||= "00" vals[:uname] ||= "wheel" vals[:gname] ||= "wheel" vals[:devmajor] ||= 0 vals[:devminor] ||= 0 FIELDS.each do |name| instance_variable_set "@#{name}", vals[name] end @empty = vals[:empty] end ## # Is the tar entry empty? def empty? @empty end def ==(other) # :nodoc: self.class === other and @checksum == other.checksum and @devmajor == other.devmajor and @devminor == other.devminor and @gid == other.gid and @gname == other.gname and @linkname == other.linkname and @magic == other.magic and @mode == other.mode and @mtime == other.mtime and @name == other.name and @prefix == other.prefix and @size == other.size and @typeflag == other.typeflag and @uid == other.uid and @uname == other.uname and @version == other.version end def to_s # :nodoc: update_checksum header end ## # Updates the TarHeader's checksum def update_checksum header = header " " * 8 @checksum = oct calculate_checksum(header), 6 end private def calculate_checksum(header) header.unpack("C*").inject {|a, b| a + b } end def header(checksum = @checksum) header = [ name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11), oct(mtime, 11), checksum, " ", typeflag, linkname, magic, oct(version, 2), uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix, ] header = header.pack PACK_FORMAT header << ("\0" * ((512 - header.size) % 512)) end def oct(num, len) "%0#{len}o" % num end end rubygems/rubygems/package/tar_reader.rb 0000644 00000004542 15102661023 0014267 0 ustar 00 # frozen_string_literal: true #-- # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. #++ ## # TarReader reads tar files and allows iteration over their items class Gem::Package::TarReader include Enumerable ## # Raised if the tar IO is not seekable class UnexpectedEOF < StandardError; end ## # Creates a new TarReader on +io+ and yields it to the block, if given. def self.new(io) reader = super return reader unless block_given? begin yield reader ensure reader.close end nil end ## # Creates a new tar file reader on +io+ which needs to respond to #pos, # #eof?, #read, #getc and #pos= def initialize(io) @io = io @init_pos = io.pos end ## # Close the tar file def close end ## # Iterates over files in the tarball yielding each entry def each return enum_for __method__ unless block_given? use_seek = @io.respond_to?(:seek) until @io.eof? do header = Gem::Package::TarHeader.from @io return if header.empty? entry = Gem::Package::TarReader::Entry.new header, @io size = entry.header.size yield entry skip = (512 - (size % 512)) % 512 pending = size - entry.bytes_read if use_seek begin # avoid reading if the @io supports seeking @io.seek pending, IO::SEEK_CUR pending = 0 rescue Errno::EINVAL end end # if seeking isn't supported or failed while pending > 0 do bytes_read = @io.read([pending, 4096].min).size raise UnexpectedEOF if @io.eof? pending -= bytes_read end @io.read skip # discard trailing zeros # make sure nobody can use #read, #getc or #rewind anymore entry.close end end alias each_entry each ## # NOTE: Do not call #rewind during #each def rewind if @init_pos == 0 @io.rewind else @io.pos = @init_pos end end ## # Seeks through the tar file until it finds the +entry+ with +name+ and # yields it. Rewinds the tar file to the beginning when the block # terminates. def seek(name) # :yields: entry found = find do |entry| entry.full_name == name end return unless found return yield found ensure rewind end end require_relative 'tar_reader/entry' rubygems/rubygems/package/digest_io.rb 0000644 00000002523 15102661023 0014122 0 ustar 00 # frozen_string_literal: true ## # IO wrapper that creates digests of contents written to the IO it wraps. class Gem::Package::DigestIO ## # Collected digests for wrapped writes. # # { # 'SHA1' => #<OpenSSL::Digest: [...]>, # 'SHA512' => #<OpenSSL::Digest: [...]>, # } attr_reader :digests ## # Wraps +io+ and updates digest for each of the digest algorithms in # the +digests+ Hash. Returns the digests hash. Example: # # io = StringIO.new # digests = { # 'SHA1' => OpenSSL::Digest.new('SHA1'), # 'SHA512' => OpenSSL::Digest.new('SHA512'), # } # # Gem::Package::DigestIO.wrap io, digests do |digest_io| # digest_io.write "hello" # end # # digests['SHA1'].hexdigest #=> "aaf4c61d[...]" # digests['SHA512'].hexdigest #=> "9b71d224[...]" def self.wrap(io, digests) digest_io = new io, digests yield digest_io return digests end ## # Creates a new DigestIO instance. Using ::wrap is recommended, see the # ::wrap documentation for documentation of +io+ and +digests+. def initialize(io, digests) @io = io @digests = digests end ## # Writes +data+ to the underlying IO and updates the digests def write(data) result = @io.write data @digests.each do |_, digest| digest << data end result end end rubygems/rubygems/package/file_source.rb 0000644 00000001141 15102661023 0014446 0 ustar 00 # frozen_string_literal: true ## # The primary source of gems is a file on disk, including all usages # internal to rubygems. # # This is a private class, do not depend on it directly. Instead, pass a path # object to `Gem::Package.new`. class Gem::Package::FileSource < Gem::Package::Source # :nodoc: all attr_reader :path def initialize(path) @path = path end def start @start ||= File.read path, 20 end def present? File.exist? path end def with_write_io(&block) File.open path, 'wb', &block end def with_read_io(&block) File.open path, 'rb', &block end end rubygems/rubygems/package/tar_writer.rb 0000644 00000017012 15102661023 0014335 0 ustar 00 # frozen_string_literal: true #-- # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. #++ ## # Allows writing of tar files class Gem::Package::TarWriter class FileOverflow < StandardError; end ## # IO wrapper that allows writing a limited amount of data class BoundedStream ## # Maximum number of bytes that can be written attr_reader :limit ## # Number of bytes written attr_reader :written ## # Wraps +io+ and allows up to +limit+ bytes to be written def initialize(io, limit) @io = io @limit = limit @written = 0 end ## # Writes +data+ onto the IO, raising a FileOverflow exception if the # number of bytes will be more than #limit def write(data) if data.bytesize + @written > @limit raise FileOverflow, "You tried to feed more data than fits in the file." end @io.write data @written += data.bytesize data.bytesize end end ## # IO wrapper that provides only #write class RestrictedStream ## # Creates a new RestrictedStream wrapping +io+ def initialize(io) @io = io end ## # Writes +data+ onto the IO def write(data) @io.write data end end ## # Creates a new TarWriter, yielding it if a block is given def self.new(io) writer = super return writer unless block_given? begin yield writer ensure writer.close end nil end ## # Creates a new TarWriter that will write to +io+ def initialize(io) @io = io @closed = false end ## # Adds file +name+ with permissions +mode+, and yields an IO for writing the # file to def add_file(name, mode) # :yields: io check_closed name, prefix = split_name name init_pos = @io.pos @io.write Gem::Package::TarHeader::EMPTY_HEADER # placeholder for the header yield RestrictedStream.new(@io) if block_given? size = @io.pos - init_pos - 512 remainder = (512 - (size % 512)) % 512 @io.write "\0" * remainder final_pos = @io.pos @io.pos = init_pos header = Gem::Package::TarHeader.new :name => name, :mode => mode, :size => size, :prefix => prefix, :mtime => Gem.source_date_epoch @io.write header @io.pos = final_pos self end ## # Adds +name+ with permissions +mode+ to the tar, yielding +io+ for writing # the file. The +digest_algorithm+ is written to a read-only +name+.sum # file following the given file contents containing the digest name and # hexdigest separated by a tab. # # The created digest object is returned. def add_file_digest(name, mode, digest_algorithms) # :yields: io digests = digest_algorithms.map do |digest_algorithm| digest = digest_algorithm.new digest_name = if digest.respond_to? :name digest.name else digest_algorithm.class.name[/::([^:]+)\z/, 1] end [digest_name, digest] end digests = Hash[*digests.flatten] add_file name, mode do |io| Gem::Package::DigestIO.wrap io, digests do |digest_io| yield digest_io end end digests end ## # Adds +name+ with permissions +mode+ to the tar, yielding +io+ for writing # the file. The +signer+ is used to add a digest file using its # digest_algorithm per add_file_digest and a cryptographic signature in # +name+.sig. If the signer has no key only the checksum file is added. # # Returns the digest. def add_file_signed(name, mode, signer) digest_algorithms = [ signer.digest_algorithm, Gem::Security.create_digest('SHA512'), ].compact.uniq digests = add_file_digest name, mode, digest_algorithms do |io| yield io end signature_digest = digests.values.compact.find do |digest| digest_name = if digest.respond_to? :name digest.name else digest.class.name[/::([^:]+)\z/, 1] end digest_name == signer.digest_name end raise "no #{signer.digest_name} in #{digests.values.compact}" unless signature_digest if signer.key signature = signer.sign signature_digest.digest add_file_simple "#{name}.sig", 0444, signature.length do |io| io.write signature end end digests end ## # Add file +name+ with permissions +mode+ +size+ bytes long. Yields an IO # to write the file to. def add_file_simple(name, mode, size) # :yields: io check_closed name, prefix = split_name name header = Gem::Package::TarHeader.new(:name => name, :mode => mode, :size => size, :prefix => prefix, :mtime => Gem.source_date_epoch).to_s @io.write header os = BoundedStream.new @io, size yield os if block_given? min_padding = size - os.written @io.write("\0" * min_padding) remainder = (512 - (size % 512)) % 512 @io.write("\0" * remainder) self end ## # Adds symlink +name+ with permissions +mode+, linking to +target+. def add_symlink(name, target, mode) check_closed name, prefix = split_name name header = Gem::Package::TarHeader.new(:name => name, :mode => mode, :size => 0, :typeflag => "2", :linkname => target, :prefix => prefix, :mtime => Gem.source_date_epoch).to_s @io.write header self end ## # Raises IOError if the TarWriter is closed def check_closed raise IOError, "closed #{self.class}" if closed? end ## # Closes the TarWriter def close check_closed @io.write "\0" * 1024 flush @closed = true end ## # Is the TarWriter closed? def closed? @closed end ## # Flushes the TarWriter's IO def flush check_closed @io.flush if @io.respond_to? :flush end ## # Creates a new directory in the tar file +name+ with +mode+ def mkdir(name, mode) check_closed name, prefix = split_name(name) header = Gem::Package::TarHeader.new :name => name, :mode => mode, :typeflag => "5", :size => 0, :prefix => prefix, :mtime => Gem.source_date_epoch @io.write header self end ## # Splits +name+ into a name and prefix that can fit in the TarHeader def split_name(name) # :nodoc: if name.bytesize > 256 raise Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)") end prefix = '' if name.bytesize > 100 parts = name.split('/', -1) # parts are never empty here name = parts.pop # initially empty for names with a trailing slash ("foo/.../bar/") prefix = parts.join('/') # if empty, then it's impossible to split (parts is empty too) while !parts.empty? && (prefix.bytesize > 155 || name.empty?) name = parts.pop + '/' + name prefix = parts.join('/') end if name.bytesize > 100 or prefix.empty? raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)") end if prefix.bytesize > 155 raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long base path (should be 155 or less)") end end return name, prefix end end rubygems/rubygems/package/old.rb 0000644 00000007201 15102661023 0012730 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ ## # The format class knows the guts of the ancient .gem file format and provides # the capability to read such ancient gems. # # Please pretend this doesn't exist. class Gem::Package::Old < Gem::Package undef_method :spec= ## # Creates a new old-format package reader for +gem+. Old-format packages # cannot be written. def initialize(gem, security_policy) require 'fileutils' require 'zlib' Gem.load_yaml @contents = nil @gem = gem @security_policy = security_policy @spec = nil end ## # A list of file names contained in this gem def contents verify return @contents if @contents @gem.with_read_io do |io| read_until_dashes io # spec header = file_list io @contents = header.map {|file| file['path'] } end end ## # Extracts the files in this package into +destination_dir+ def extract_files(destination_dir) verify errstr = "Error reading files from gem" @gem.with_read_io do |io| read_until_dashes io # spec header = file_list io raise Gem::Exception, errstr unless header header.each do |entry| full_name = entry['path'] destination = install_location full_name, destination_dir file_data = String.new read_until_dashes io do |line| file_data << line end file_data = file_data.strip.unpack("m")[0] file_data = Zlib::Inflate.inflate file_data raise Gem::Package::FormatError, "#{full_name} in #{@gem} is corrupt" if file_data.length != entry['size'].to_i FileUtils.rm_rf destination FileUtils.mkdir_p File.dirname(destination), :mode => dir_mode && 0755 File.open destination, 'wb', file_mode(entry['mode']) do |out| out.write file_data end verbose destination end end rescue Zlib::DataError raise Gem::Exception, errstr end ## # Reads the file list section from the old-format gem +io+ def file_list(io) # :nodoc: header = String.new read_until_dashes io do |line| header << line end Gem::SafeYAML.safe_load header end ## # Reads lines until a "---" separator is found def read_until_dashes(io) # :nodoc: while (line = io.gets) && line.chomp.strip != "---" do yield line if block_given? end end ## # Skips the Ruby self-install header in +io+. def skip_ruby(io) # :nodoc: loop do line = io.gets return if line.chomp == '__END__' break unless line end raise Gem::Exception, "Failed to find end of Ruby script while reading gem" end ## # The specification for this gem def spec verify return @spec if @spec yaml = String.new @gem.with_read_io do |io| skip_ruby io read_until_dashes io do |line| yaml << line end end begin @spec = Gem::Specification.from_yaml yaml rescue YAML::SyntaxError raise Gem::Exception, "Failed to parse gem specification out of gem file" end rescue ArgumentError raise Gem::Exception, "Failed to parse gem specification out of gem file" end ## # Raises an exception if a security policy that verifies data is active. # Old format gems cannot be verified as signed. def verify return true unless @security_policy raise Gem::Security::Exception, 'old format gems do not contain signatures and cannot be verified' if @security_policy.verify_data true end end rubygems/rubygems/package_task.rb 0000644 00000007454 15102661023 0013206 0 ustar 00 # frozen_string_literal: true # Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. require_relative '../rubygems' require_relative 'package' require 'rake/packagetask' ## # Create a package based upon a Gem::Specification. Gem packages, as well as # zip files and tar/gzipped packages can be produced by this task. # # In addition to the Rake targets generated by Rake::PackageTask, a # Gem::PackageTask will also generate the following tasks: # # [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.gem"</b>] # Create a RubyGems package with the given name and version. # # Example using a Gem::Specification: # # require 'rubygems' # require 'rubygems/package_task' # # spec = Gem::Specification.new do |s| # s.summary = "Ruby based make-like utility." # s.name = 'rake' # s.version = PKG_VERSION # s.requirements << 'none' # s.files = PKG_FILES # s.description = <<-EOF # Rake is a Make-like program implemented in Ruby. Tasks # and dependencies are specified in standard Ruby syntax. # EOF # end # # Gem::PackageTask.new(spec) do |pkg| # pkg.need_zip = true # pkg.need_tar = true # end class Gem::PackageTask < Rake::PackageTask ## # Ruby Gem::Specification containing the metadata for this package. The # name, version and package_files are automatically determined from the # gemspec and don't need to be explicitly provided. attr_accessor :gem_spec ## # Create a Gem Package task library. Automatically define the gem if a # block is given. If no block is supplied, then #define needs to be called # to define the task. def initialize(gem_spec) init gem_spec yield self if block_given? define if block_given? end ## # Initialization tasks without the "yield self" or define operations. def init(gem) super gem.full_name, :noversion @gem_spec = gem @package_files += gem_spec.files if gem_spec.files @fileutils_output = $stdout end ## # Create the Rake tasks and actions specified by this Gem::PackageTask. # (+define+ is automatically called if a block is given to +new+). def define super gem_file = File.basename gem_spec.cache_file gem_path = File.join package_dir, gem_file gem_dir = File.join package_dir, gem_spec.full_name task :package => [:gem] directory package_dir directory gem_dir desc "Build the gem file #{gem_file}" task :gem => [gem_path] trace = Rake.application.options.trace Gem.configuration.verbose = trace file gem_path => [package_dir, gem_dir] + @gem_spec.files do chdir(gem_dir) do when_writing "Creating #{gem_spec.file_name}" do Gem::Package.build gem_spec verbose trace do mv gem_file, '..' end end end end end end rubygems/rubygems/request.rb 0000644 00000021171 15102661023 0012251 0 ustar 00 # frozen_string_literal: true require 'net/http' require_relative 'user_interaction' class Gem::Request extend Gem::UserInteraction include Gem::UserInteraction ### # Legacy. This is used in tests. def self.create_with_proxy(uri, request_class, last_modified, proxy) # :nodoc: cert_files = get_cert_files proxy ||= get_proxy_from_env(uri.scheme) pool = ConnectionPools.new proxy_uri(proxy), cert_files new(uri, request_class, last_modified, pool.pool_for(uri)) end def self.proxy_uri(proxy) # :nodoc: require "uri" case proxy when :no_proxy then nil when URI::HTTP then proxy else URI.parse(proxy) end end def initialize(uri, request_class, last_modified, pool) @uri = uri @request_class = request_class @last_modified = last_modified @requests = Hash.new 0 @user_agent = user_agent @connection_pool = pool end def proxy_uri; @connection_pool.proxy_uri; end def cert_files; @connection_pool.cert_files; end def self.get_cert_files pattern = File.expand_path("./ssl_certs/*/*.pem", File.dirname(__FILE__)) Dir.glob(pattern) end def self.configure_connection_for_https(connection, cert_files) raise Gem::Exception.new('OpenSSL is not available. Install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources') unless Gem::HAVE_OPENSSL connection.use_ssl = true connection.verify_mode = Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER store = OpenSSL::X509::Store.new if Gem.configuration.ssl_client_cert pem = File.read Gem.configuration.ssl_client_cert connection.cert = OpenSSL::X509::Certificate.new pem connection.key = OpenSSL::PKey::RSA.new pem end store.set_default_paths cert_files.each do |ssl_cert_file| store.add_file ssl_cert_file end if Gem.configuration.ssl_ca_cert if File.directory? Gem.configuration.ssl_ca_cert store.add_path Gem.configuration.ssl_ca_cert else store.add_file Gem.configuration.ssl_ca_cert end end connection.cert_store = store connection.verify_callback = proc do |preverify_ok, store_context| verify_certificate store_context unless preverify_ok preverify_ok end connection end def self.verify_certificate(store_context) depth = store_context.error_depth error = store_context.error_string number = store_context.error cert = store_context.current_cert ui.alert_error "SSL verification error at depth #{depth}: #{error} (#{number})" extra_message = verify_certificate_message number, cert ui.alert_error extra_message if extra_message end def self.verify_certificate_message(error_number, cert) return unless cert case error_number when OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED then require 'time' "Certificate #{cert.subject} expired at #{cert.not_after.iso8601}" when OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID then require 'time' "Certificate #{cert.subject} not valid until #{cert.not_before.iso8601}" when OpenSSL::X509::V_ERR_CERT_REJECTED then "Certificate #{cert.subject} is rejected" when OpenSSL::X509::V_ERR_CERT_UNTRUSTED then "Certificate #{cert.subject} is not trusted" when OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT then "Certificate #{cert.issuer} is not trusted" when OpenSSL::X509::V_ERR_INVALID_CA then "Certificate #{cert.subject} is an invalid CA certificate" when OpenSSL::X509::V_ERR_INVALID_PURPOSE then "Certificate #{cert.subject} has an invalid purpose" when OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN then "Root certificate is not trusted (#{cert.subject})" when OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY then "You must add #{cert.issuer} to your local trusted store" when OpenSSL::X509::V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE then "Cannot verify certificate issued by #{cert.issuer}" end end ## # Creates or an HTTP connection based on +uri+, or retrieves an existing # connection, using a proxy if needed. def connection_for(uri) @connection_pool.checkout rescue Gem::HAVE_OPENSSL ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN, Errno::EHOSTDOWN => e raise Gem::RemoteFetcher::FetchError.new(e.message, uri) end def fetch request = @request_class.new @uri.request_uri unless @uri.nil? || @uri.user.nil? || @uri.user.empty? request.basic_auth Gem::UriFormatter.new(@uri.user).unescape, Gem::UriFormatter.new(@uri.password).unescape end request.add_field 'User-Agent', @user_agent request.add_field 'Connection', 'keep-alive' request.add_field 'Keep-Alive', '30' if @last_modified require 'time' request.add_field 'If-Modified-Since', @last_modified.httpdate end yield request if block_given? perform_request request end ## # Returns a proxy URI for the given +scheme+ if one is set in the # environment variables. def self.get_proxy_from_env(scheme = 'http') _scheme = scheme.downcase _SCHEME = scheme.upcase env_proxy = ENV["#{_scheme}_proxy"] || ENV["#{_SCHEME}_PROXY"] no_env_proxy = env_proxy.nil? || env_proxy.empty? if no_env_proxy return (_scheme == 'https' || _scheme == 'http') ? :no_proxy : get_proxy_from_env('http') end require "uri" uri = URI(Gem::UriFormatter.new(env_proxy).normalize) if uri and uri.user.nil? and uri.password.nil? user = ENV["#{_scheme}_proxy_user"] || ENV["#{_SCHEME}_PROXY_USER"] password = ENV["#{_scheme}_proxy_pass"] || ENV["#{_SCHEME}_PROXY_PASS"] uri.user = Gem::UriFormatter.new(user).escape uri.password = Gem::UriFormatter.new(password).escape end uri end def perform_request(request) # :nodoc: connection = connection_for @uri retried = false bad_response = false begin @requests[connection.object_id] += 1 verbose "#{request.method} #{Gem::Uri.new(@uri).redacted}" file_name = File.basename(@uri.path) # perform download progress reporter only for gems if request.response_body_permitted? && file_name =~ /\.gem$/ reporter = ui.download_reporter response = connection.request(request) do |incomplete_response| if Net::HTTPOK === incomplete_response reporter.fetch(file_name, incomplete_response.content_length) downloaded = 0 data = String.new incomplete_response.read_body do |segment| data << segment downloaded += segment.length reporter.update(downloaded) end reporter.done if incomplete_response.respond_to? :body= incomplete_response.body = data else incomplete_response.instance_variable_set(:@body, data) end end end else response = connection.request request end verbose "#{response.code} #{response.message}" rescue Net::HTTPBadResponse verbose "bad response" reset connection raise Gem::RemoteFetcher::FetchError.new('too many bad responses', @uri) if bad_response bad_response = true retry rescue Net::HTTPFatalError verbose "fatal error" raise Gem::RemoteFetcher::FetchError.new('fatal error', @uri) # HACK work around EOFError bug in Net::HTTP # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible # to install gems. rescue EOFError, Timeout::Error, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE requests = @requests[connection.object_id] verbose "connection reset after #{requests} requests, retrying" raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried reset connection retried = true retry end response ensure @connection_pool.checkin connection end ## # Resets HTTP connection +connection+. def reset(connection) @requests.delete connection.object_id connection.finish connection.start end def user_agent ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}".dup ruby_version = RUBY_VERSION ruby_version += 'dev' if RUBY_PATCHLEVEL == -1 ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}" if RUBY_PATCHLEVEL >= 0 ua << " patchlevel #{RUBY_PATCHLEVEL}" elsif defined?(RUBY_REVISION) ua << " revision #{RUBY_REVISION}" end ua << ")" ua << " #{RUBY_ENGINE}" if RUBY_ENGINE != 'ruby' ua end end require_relative 'request/http_pool' require_relative 'request/https_pool' require_relative 'request/connection_pools' rubygems/rubygems/version.rb 0000644 00000030702 15102661023 0012246 0 ustar 00 # frozen_string_literal: true ## # The Version class processes string versions into comparable # values. A version string should normally be a series of numbers # separated by periods. Each part (digits separated by periods) is # considered its own number, and these are used for sorting. So for # instance, 3.10 sorts higher than 3.2 because ten is greater than # two. # # If any part contains letters (currently only a-z are supported) then # that version is considered prerelease. Versions with a prerelease # part in the Nth part sort less than versions with N-1 # parts. Prerelease parts are sorted alphabetically using the normal # Ruby string sorting rules. If a prerelease part contains both # letters and numbers, it will be broken into multiple parts to # provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is # greater than 1.0.a9). # # Prereleases sort between real releases (newest to oldest): # # 1. 1.0 # 2. 1.0.b1 # 3. 1.0.a.2 # 4. 0.9 # # If you want to specify a version restriction that includes both prereleases # and regular releases of the 1.x series this is the best way: # # s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0' # # == How Software Changes # # Users expect to be able to specify a version constraint that gives them # some reasonable expectation that new versions of a library will work with # their software if the version constraint is true, and not work with their # software if the version constraint is false. In other words, the perfect # system will accept all compatible versions of the library and reject all # incompatible versions. # # Libraries change in 3 ways (well, more than 3, but stay focused here!). # # 1. The change may be an implementation detail only and have no effect on # the client software. # 2. The change may add new features, but do so in a way that client software # written to an earlier version is still compatible. # 3. The change may change the public interface of the library in such a way # that old software is no longer compatible. # # Some examples are appropriate at this point. Suppose I have a Stack class # that supports a <tt>push</tt> and a <tt>pop</tt> method. # # === Examples of Category 1 changes: # # * Switch from an array based implementation to a linked-list based # implementation. # * Provide an automatic (and transparent) backing store for large stacks. # # === Examples of Category 2 changes might be: # # * Add a <tt>depth</tt> method to return the current depth of the stack. # * Add a <tt>top</tt> method that returns the current top of stack (without # changing the stack). # * Change <tt>push</tt> so that it returns the item pushed (previously it # had no usable return value). # # === Examples of Category 3 changes might be: # # * Changes <tt>pop</tt> so that it no longer returns a value (you must use # <tt>top</tt> to get the top of the stack). # * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>. # # == RubyGems Rational Versioning # # * Versions shall be represented by three non-negative integers, separated # by periods (e.g. 3.1.4). The first integers is the "major" version # number, the second integer is the "minor" version number, and the third # integer is the "build" number. # # * A category 1 change (implementation detail) will increment the build # number. # # * A category 2 change (backwards compatible) will increment the minor # version number and reset the build number. # # * A category 3 change (incompatible) will increment the major build number # and reset the minor and build numbers. # # * Any "public" release of a gem should have a different version. Normally # that means incrementing the build number. This means a developer can # generate builds all day long, but as soon as they make a public release, # the version must be updated. # # === Examples # # Let's work through a project lifecycle using our Stack example from above. # # Version 0.0.1:: The initial Stack class is release. # Version 0.0.2:: Switched to a linked=list implementation because it is # cooler. # Version 0.1.0:: Added a <tt>depth</tt> method. # Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil # (<tt>pop</tt> used to return the old top item). # Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it # return nil). # Version 1.1.1:: Fixed a bug in the linked list implementation. # Version 1.1.2:: Fixed a bug introduced in the last fix. # # Client A needs a stack with basic push/pop capability. They write to the # original interface (no <tt>top</tt>), so their version constraint looks like: # # gem 'stack', '>= 0.0' # # Essentially, any version is OK with Client A. An incompatible change to # the library will cause them grief, but they are willing to take the chance # (we call Client A optimistic). # # Client B is just like Client A except for two things: (1) They use the # <tt>depth</tt> method and (2) they are worried about future # incompatibilities, so they write their version constraint like this: # # gem 'stack', '~> 0.1' # # The <tt>depth</tt> method was introduced in version 0.1.0, so that version # or anything later is fine, as long as the version stays below version 1.0 # where incompatibilities are introduced. We call Client B pessimistic # because they are worried about incompatible future changes (it is OK to be # pessimistic!). # # == Preventing Version Catastrophe: # # From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html # # Let's say you're depending on the fnord gem version 2.y.z. If you # specify your dependency as ">= 2.0.0" then, you're good, right? What # happens if fnord 3.0 comes out and it isn't backwards compatible # with 2.y.z? Your stuff will break as a result of using ">=". The # better route is to specify your dependency with an "approximate" version # specifier ("~>"). They're a tad confusing, so here is how the dependency # specifiers work: # # Specification From ... To (exclusive) # ">= 3.0" 3.0 ... ∞ # "~> 3.0" 3.0 ... 4.0 # "~> 3.0.0" 3.0.0 ... 3.1 # "~> 3.5" 3.5 ... 4.0 # "~> 3.5.0" 3.5.0 ... 3.6 # "~> 3" 3.0 ... 4.0 # # For the last example, single-digit versions are automatically extended with # a zero to give a sensible result. class Gem::Version autoload :Requirement, File.expand_path('requirement', __dir__) include Comparable VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?'.freeze # :nodoc: ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/.freeze # :nodoc: ## # A string representation of this Version. def version @version.dup end alias to_s version ## # True if the +version+ string matches RubyGems' requirements. def self.correct?(version) unless Gem::Deprecate.skip warn "nil versions are discouraged and will be deprecated in Rubygems 4" if version.nil? end !!(version.to_s =~ ANCHORED_VERSION_PATTERN) end ## # Factory method to create a Version object. Input may be a Version # or a String. Intended to simplify client code. # # ver1 = Version.create('1.3.17') # -> (Version object) # ver2 = Version.create(ver1) # -> (ver1) # ver3 = Version.create(nil) # -> nil def self.create(input) if self === input # check yourself before you wreck yourself input elsif input.nil? nil else new input end end @@all = {} @@bump = {} @@release = {} def self.new(version) # :nodoc: return super unless Gem::Version == self @@all[version] ||= super end ## # Constructs a Version from the +version+ string. A version string is a # series of digits or ASCII letters separated by dots. def initialize(version) unless self.class.correct?(version) raise ArgumentError, "Malformed version number string #{version}" end # If version is an empty string convert it to 0 version = 0 if version.is_a?(String) && version =~ /\A\s*\Z/ @version = version.to_s.strip.gsub("-",".pre.") @segments = nil end ## # Return a new version object where the next to the last revision # number is one greater (e.g., 5.3.1 => 5.4). # # Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored. def bump @@bump[self] ||= begin segments = self.segments segments.pop while segments.any? {|s| String === s } segments.pop if segments.size > 1 segments[-1] = segments[-1].succ self.class.new segments.join(".") end end ## # A Version is only eql? to another version if it's specified to the # same precision. Version "1.0" is not the same as version "1". def eql?(other) self.class === other and @version == other._version end def hash # :nodoc: canonical_segments.hash end def init_with(coder) # :nodoc: yaml_initialize coder.tag, coder.map end def inspect # :nodoc: "#<#{self.class} #{version.inspect}>" end ## # Dump only the raw version string, not the complete object. It's a # string for backwards (RubyGems 1.3.5 and earlier) compatibility. def marshal_dump [version] end ## # Load custom marshal format. It's a string for backwards (RubyGems # 1.3.5 and earlier) compatibility. def marshal_load(array) initialize array[0] end def yaml_initialize(tag, map) # :nodoc: @version = map['version'] @segments = nil @hash = nil end def to_yaml_properties # :nodoc: ["@version"] end def encode_with(coder) # :nodoc: coder.add 'version', @version end ## # A version is considered a prerelease if it contains a letter. def prerelease? unless instance_variable_defined? :@prerelease @prerelease = !!(@version =~ /[a-zA-Z]/) end @prerelease end def pretty_print(q) # :nodoc: q.text "Gem::Version.new(#{version.inspect})" end ## # The release for this version (e.g. 1.2.0.a -> 1.2.0). # Non-prerelease versions return themselves. def release @@release[self] ||= if prerelease? segments = self.segments segments.pop while segments.any? {|s| String === s } self.class.new segments.join('.') else self end end def segments # :nodoc: _segments.dup end ## # A recommended version for use with a ~> Requirement. def approximate_recommendation segments = self.segments segments.pop while segments.any? {|s| String === s } segments.pop while segments.size > 2 segments.push 0 while segments.size < 2 recommendation = "~> #{segments.join(".")}" recommendation += ".a" if prerelease? recommendation end ## # Compares this version with +other+ returning -1, 0, or 1 if the # other version is larger, the same, or smaller than this # one. Attempts to compare to something that's not a # <tt>Gem::Version</tt> return +nil+. def <=>(other) return unless Gem::Version === other return 0 if @version == other._version || canonical_segments == other.canonical_segments lhsegments = canonical_segments rhsegments = other.canonical_segments lhsize = lhsegments.size rhsize = rhsegments.size limit = (lhsize > rhsize ? lhsize : rhsize) - 1 i = 0 while i <= limit lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0 i += 1 next if lhs == rhs return -1 if String === lhs && Numeric === rhs return 1 if Numeric === lhs && String === rhs return lhs <=> rhs end return 0 end def canonical_segments @canonical_segments ||= _split_segments.map! do |segments| segments.reverse_each.drop_while {|s| s == 0 }.reverse end.reduce(&:concat) end def freeze prerelease? canonical_segments super end protected def _version @version end def _segments # segments is lazy so it can pick up version values that come from # old marshaled versions, which don't go through marshal_load. # since this version object is cached in @@all, its @segments should be frozen @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s| /^\d+$/ =~ s ? s.to_i : s end.freeze end def _split_segments string_start = _segments.index {|s| s.is_a?(String) } string_segments = segments numeric_segments = string_segments.slice!(0, string_start || string_segments.size) return numeric_segments, string_segments end end rubygems/rubygems/server.rb 0000644 00000055570 15102661023 0012101 0 ustar 00 # frozen_string_literal: true require 'zlib' require 'erb' require 'uri' require_relative '../rubygems' require_relative 'rdoc' ## # Gem::Server and allows users to serve gems for consumption by # `gem --remote-install`. # # gem_server starts an HTTP server on the given port and serves the following: # * "/" - Browsing of gem spec files for installed gems # * "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index # * "/latest_specs.#{Gem.marshal_version}.gz" - latest specs # name/version/platform index # * "/quick/" - Individual gemspecs # * "/gems" - Direct access to download the installable gems # * "/rdoc?q=" - Search for installed rdoc documentation # # == Usage # # gem_server = Gem::Server.new Gem.dir, 8089, false # gem_server.run # #-- # TODO Refactor into a real WEBrick servlet to remove code duplication. class Gem::Server attr_reader :spec_dirs include ERB::Util include Gem::UserInteraction SEARCH = <<-ERB.freeze <form class="headerSearch" name="headerSearchForm" method="get" action="/rdoc"> <div id="search" style="float:right"> <label for="q">Filter/Search</label> <input id="q" type="text" style="width:10em" name="q"> <button type="submit" style="display:none"></button> </div> </form> ERB DOC_TEMPLATE = <<-'ERB'.freeze <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>RubyGems Documentation Index</title> <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" /> </head> <body> <div id="fileHeader"> <%= SEARCH %> <h1>RubyGems Documentation Index</h1> </div> <!-- banner header --> <div id="bodyContent"> <div id="contextContent"> <div id="description"> <h1>Summary</h1> <p>There are <%=values["gem_count"]%> gems installed:</p> <p> <%= values["specs"].map { |v| "<a href=\"##{u v["name"]}\">#{h v["name"]}</a>" }.join ', ' %>. <h1>Gems</h1> <dl> <% values["specs"].each do |spec| %> <dt> <% if spec["first_name_entry"] then %> <a name="<%=h spec["name"]%>"></a> <% end %> <b><%=h spec["name"]%> <%=h spec["version"]%></b> <% if spec["ri_installed"] || spec["rdoc_installed"] then %> <a href="<%=spec["doc_path"]%>">[rdoc]</a> <% else %> <span title="rdoc not installed">[rdoc]</span> <% end %> <% if spec["homepage"] then %> <a href="<%=uri_encode spec["homepage"]%>" title="<%=h spec["homepage"]%>">[www]</a> <% else %> <span title="no homepage available">[www]</span> <% end %> <% if spec["has_deps"] then %> - depends on <%= spec["dependencies"].map { |v| "<a href=\"##{u v["name"]}\">#{h v["name"]}</a>" }.join ', ' %>. <% end %> </dt> <dd> <%=spec["summary"]%> <% if spec["executables"] then %> <br/> <% if spec["only_one_executable"] then %> Executable is <% else %> Executables are <%end%> <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{h v["executable"]}</span>"}.join ', ' %>. <%end%> <br/> <br/> </dd> <% end %> </dl> </div> </div> </div> <div id="validator-badges"> <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p> </div> </body> </html> ERB # CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108 RDOC_CSS = <<-CSS.freeze body { font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 90%; margin: 0; margin-left: 40px; padding: 0; background: white; } h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } h1 { font-size: 150%; } h2,h3,h4 { margin-top: 1em; } a { background: #eef; color: #039; text-decoration: none; } a:hover { background: #039; color: #eef; } /* Override the base stylesheets Anchor inside a table cell */ td > a { background: transparent; color: #039; text-decoration: none; } /* and inside a section title */ .section-title > a { background: transparent; color: #eee; text-decoration: none; } /* === Structural elements =================================== */ div#index { margin: 0; margin-left: -40px; padding: 0; font-size: 90%; } div#index a { margin-left: 0.7em; } div#index .section-bar { margin-left: 0px; padding-left: 0.7em; background: #ccc; font-size: small; } div#classHeader, div#fileHeader { width: auto; color: white; padding: 0.5em 1.5em 0.5em 1.5em; margin: 0; margin-left: -40px; border-bottom: 3px solid #006; } div#classHeader a, div#fileHeader a { background: inherit; color: white; } div#classHeader td, div#fileHeader td { background: inherit; color: white; } div#fileHeader { background: #057; } div#classHeader { background: #048; } .class-name-in-header { font-size: 180%; font-weight: bold; } div#bodyContent { padding: 0 1.5em 0 1.5em; } div#description { padding: 0.5em 1.5em; background: #efefef; border: 1px dotted #999; } div#description h1,h2,h3,h4,h5,h6 { color: #125;; background: transparent; } div#validator-badges { text-align: center; } div#validator-badges img { border: 0; } div#copyright { color: #333; background: #efefef; font: 0.75em sans-serif; margin-top: 5em; margin-bottom: 0; padding: 0.5em 2em; } /* === Classes =================================== */ table.header-table { color: white; font-size: small; } .type-note { font-size: small; color: #DEDEDE; } .xxsection-bar { background: #eee; color: #333; padding: 3px; } .section-bar { color: #333; border-bottom: 1px solid #999; margin-left: -20px; } .section-title { background: #79a; color: #eee; padding: 3px; margin-top: 2em; margin-left: -30px; border: 1px solid #999; } .top-aligned-row { vertical-align: top } .bottom-aligned-row { vertical-align: bottom } /* --- Context section classes ----------------------- */ .context-row { } .context-item-name { font-family: monospace; font-weight: bold; color: black; } .context-item-value { font-size: small; color: #448; } .context-item-desc { color: #333; padding-left: 2em; } /* --- Method classes -------------------------- */ .method-detail { background: #efefef; padding: 0; margin-top: 0.5em; margin-bottom: 1em; border: 1px dotted #ccc; } .method-heading { color: black; background: #ccc; border-bottom: 1px solid #666; padding: 0.2em 0.5em 0 0.5em; } .method-signature { color: black; background: inherit; } .method-name { font-weight: bold; } .method-args { font-style: italic; } .method-description { padding: 0 0.5em 0 0.5em; } /* --- Source code sections -------------------- */ a.source-toggle { font-size: 90%; } div.method-source-code { background: #262626; color: #ffdead; margin: 1em; padding: 0.5em; border: 1px dashed #999; overflow: hidden; } div.method-source-code pre { color: #ffdead; overflow: hidden; } /* --- Ruby keyword styles --------------------- */ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; } .ruby-constant { color: #7fffd4; background: transparent; } .ruby-keyword { color: #00ffff; background: transparent; } .ruby-ivar { color: #eedd82; background: transparent; } .ruby-operator { color: #00ffee; background: transparent; } .ruby-identifier { color: #ffdead; background: transparent; } .ruby-node { color: #ffa07a; background: transparent; } .ruby-comment { color: #b22222; font-weight: bold; background: transparent; } .ruby-regexp { color: #ffa07a; background: transparent; } .ruby-value { color: #7fffd4; background: transparent; } CSS RDOC_NO_DOCUMENTATION = <<-'ERB'.freeze <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Found documentation</title> <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" /> </head> <body> <div id="fileHeader"> <%= SEARCH %> <h1>No documentation found</h1> </div> <div id="bodyContent"> <div id="contextContent"> <div id="description"> <p>No gems matched <%= h query.inspect %></p> <p> Back to <a href="/">complete gem index</a> </p> </div> </div> </div> <div id="validator-badges"> <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p> </div> </body> </html> ERB RDOC_SEARCH_TEMPLATE = <<-'ERB'.freeze <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Found documentation</title> <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" /> </head> <body> <div id="fileHeader"> <%= SEARCH %> <h1>Found documentation</h1> </div> <!-- banner header --> <div id="bodyContent"> <div id="contextContent"> <div id="description"> <h1>Summary</h1> <p><%=doc_items.length%> documentation topics found.</p> <h1>Topics</h1> <dl> <% doc_items.each do |doc_item| %> <dt> <b><%=doc_item[:name]%></b> <a href="<%=u doc_item[:url]%>">[rdoc]</a> </dt> <dd> <%=h doc_item[:summary]%> <br/> <br/> </dd> <% end %> </dl> <p> Back to <a href="/">complete gem index</a> </p> </div> </div> </div> <div id="validator-badges"> <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p> </div> </body> </html> ERB def self.run(options) new(options[:gemdir], options[:port], options[:daemon], options[:launch], options[:addresses]).run end def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil) begin require 'webrick' rescue LoadError abort "webrick is not found. You may need to `gem install webrick` to install webrick." end Gem::RDoc.load_rdoc Socket.do_not_reverse_lookup = true @gem_dirs = Array gem_dirs @port = port @daemon = daemon @launch = launch @addresses = addresses logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger @spec_dirs = @gem_dirs.map {|gem_dir| File.join gem_dir, 'specifications' } @spec_dirs.reject! {|spec_dir| !File.directory? spec_dir } reset_gems @have_rdoc_4_plus = nil end def add_date(res) res['date'] = @spec_dirs.map do |spec_dir| File.stat(spec_dir).mtime end.max end def uri_encode(str) str.gsub(URI::UNSAFE) do |match| match.each_byte.map {|c| sprintf('%%%02X', c.ord) }.join end end def doc_root(gem_name) if have_rdoc_4_plus? "/doc_root/#{u gem_name}/" else "/doc_root/#{u gem_name}/rdoc/index.html" end end def have_rdoc_4_plus? @have_rdoc_4_plus ||= Gem::Requirement.new('>= 4.0.0.preview2').satisfied_by? Gem::RDoc.rdoc_version end def latest_specs(req, res) reset_gems res['content-type'] = 'application/x-gzip' add_date res latest_specs = Gem::Specification.latest_specs specs = latest_specs.sort.map do |spec| platform = spec.original_platform || Gem::Platform::RUBY [spec.name, spec.version, platform] end specs = Marshal.dump specs if req.path =~ /\.gz$/ specs = Gem::Util.gzip specs res['content-type'] = 'application/x-gzip' else res['content-type'] = 'application/octet-stream' end if req.request_method == 'HEAD' res['content-length'] = specs.length else res.body << specs end end ## # Creates server sockets based on the addresses option. If no addresses # were given a server socket for all interfaces is created. def listen(addresses = @addresses) addresses = [nil] unless addresses listeners = 0 addresses.each do |address| begin @server.listen address, @port @server.listeners[listeners..-1].each do |listener| host, port = listener.addr.values_at 2, 1 host = "[#{host}]" if host =~ /:/ # we don't reverse lookup say "Server started at http://#{host}:#{port}" end listeners = @server.listeners.length rescue SystemCallError next end end if @server.listeners.empty? say "Unable to start a server." say "Check for running servers or your --bind and --port arguments" terminate_interaction 1 end end def prerelease_specs(req, res) reset_gems res['content-type'] = 'application/x-gzip' add_date res specs = Gem::Specification.select do |spec| spec.version.prerelease? end.sort.map do |spec| platform = spec.original_platform || Gem::Platform::RUBY [spec.name, spec.version, platform] end specs = Marshal.dump specs if req.path =~ /\.gz$/ specs = Gem::Util.gzip specs res['content-type'] = 'application/x-gzip' else res['content-type'] = 'application/octet-stream' end if req.request_method == 'HEAD' res['content-length'] = specs.length else res.body << specs end end def quick(req, res) reset_gems res['content-type'] = 'text/plain' add_date res case req.request_uri.path when %r{^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)\.gemspec\.rz$} then marshal_format, full_name = $1, $2 specs = Gem::Specification.find_all_by_full_name(full_name) selector = full_name.inspect if specs.empty? res.status = 404 res.body = "No gems found matching #{selector}" elsif specs.length > 1 res.status = 500 res.body = "Multiple gems found matching #{selector}" elsif marshal_format res['content-type'] = 'application/x-deflate' res.body << Gem.deflate(Marshal.dump(specs.first)) end else raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." end end def root(req, res) reset_gems add_date res raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless req.path == '/' specs = [] total_file_count = 0 Gem::Specification.each do |spec| total_file_count += spec.files.size deps = spec.dependencies.map do |dep| { "name" => dep.name, "type" => dep.type, "version" => dep.requirement.to_s, } end deps = deps.sort_by {|dep| [dep["name"].downcase, dep["version"]] } deps.last["is_last"] = true unless deps.empty? # executables executables = spec.executables.sort.collect {|exec| {"executable" => exec} } executables = nil if executables.empty? executables.last["is_last"] = true if executables # Pre-process spec homepage for safety reasons begin homepage_uri = URI.parse(spec.homepage) if [URI::HTTP, URI::HTTPS].member? homepage_uri.class homepage_uri = spec.homepage else homepage_uri = "." end rescue URI::InvalidURIError homepage_uri = "." end specs << { "authors" => spec.authors.sort.join(", "), "date" => spec.date.to_s, "dependencies" => deps, "doc_path" => doc_root(spec.full_name), "executables" => executables, "only_one_executable" => (executables && executables.size == 1), "full_name" => spec.full_name, "has_deps" => !deps.empty?, "homepage" => homepage_uri, "name" => spec.name, "rdoc_installed" => Gem::RDoc.new(spec).rdoc_installed?, "ri_installed" => Gem::RDoc.new(spec).ri_installed?, "summary" => spec.summary, "version" => spec.version.to_s, } end specs << { "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", "dependencies" => [], "doc_path" => doc_root("rubygems-#{Gem::VERSION}"), "executables" => [{"executable" => 'gem', "is_last" => true}], "only_one_executable" => true, "full_name" => "rubygems-#{Gem::VERSION}", "has_deps" => false, "homepage" => "https://guides.rubygems.org/", "name" => 'rubygems', "ri_installed" => true, "summary" => "RubyGems itself", "version" => Gem::VERSION, } specs = specs.sort_by {|spec| [spec["name"].downcase, spec["version"]] } specs.last["is_last"] = true # tag all specs with first_name_entry last_spec = nil specs.each do |spec| is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase) spec["first_name_entry"] = is_first last_spec = spec end # create page from template template = ERB.new(DOC_TEMPLATE) res['content-type'] = 'text/html' values = { "gem_count" => specs.size.to_s, "specs" => specs, "total_file_count" => total_file_count.to_s } # suppress 1.9.3dev warning about unused variable values = values result = template.result binding res.body = result end ## # Can be used for quick navigation to the rdoc documentation. You can then # define a search shortcut for your browser. E.g. in Firefox connect # 'shortcut:rdoc' to http://localhost:8808/rdoc?q=%s template. Then you can # directly open the ActionPack documentation by typing 'rdoc actionp'. If # there are multiple hits for the search term, they are presented as a list # with links. # # Search algorithm aims for an intuitive search: # 1. first try to find the gems and documentation folders which name # starts with the search term # 2. search for entries, that *contain* the search term # 3. show all the gems # # If there is only one search hit, user is immediately redirected to the # documentation for the particular gem, otherwise a list with results is # shown. # # === Additional trick - install documentation for Ruby core # # Note: please adjust paths accordingly use for example 'locate yaml.rb' and # 'gem environment' to identify directories, that are specific for your # local installation # # 1. install Ruby sources # cd /usr/src # sudo apt-get source ruby # # 2. generate documentation # rdoc -o /usr/lib/ruby/gems/1.8/doc/core/rdoc \ # /usr/lib/ruby/1.8 ruby1.8-1.8.7.72 # # By typing 'rdoc core' you can now access the core documentation def rdoc(req, res) query = req.query['q'] show_rdoc_for_pattern("#{query}*", res) && return show_rdoc_for_pattern("*#{query}*", res) && return template = ERB.new RDOC_NO_DOCUMENTATION res['content-type'] = 'text/html' res.body = template.result binding end ## # Updates the server to use the latest installed gems. def reset_gems # :nodoc: Gem::Specification.dirs = @gem_dirs end ## # Returns true and prepares http response, if rdoc for the requested gem # name pattern was found. # # The search is based on the file system content, not on the gems metadata. # This allows additional documentation folders like 'core' for the Ruby core # documentation - just put it underneath the main doc folder. def show_rdoc_for_pattern(pattern, res) found_gems = Dir.glob("{#{@gem_dirs.join ','}}/doc/#{pattern}").select do |path| File.exist? File.join(path, 'rdoc/index.html') end case found_gems.length when 0 return false when 1 new_path = File.basename(found_gems[0]) res.status = 302 res['Location'] = doc_root new_path return true else doc_items = [] found_gems.each do |file_name| base_name = File.basename(file_name) doc_items << { :name => base_name, :url => doc_root(new_path), :summary => '', } end template = ERB.new(RDOC_SEARCH_TEMPLATE) res['content-type'] = 'text/html' result = template.result binding res.body = result return true end end def run listen WEBrick::Daemon.start if @daemon @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs) @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs) @server.mount_proc "/latest_specs.#{Gem.marshal_version}", method(:latest_specs) @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz", method(:latest_specs) @server.mount_proc "/prerelease_specs.#{Gem.marshal_version}", method(:prerelease_specs) @server.mount_proc "/prerelease_specs.#{Gem.marshal_version}.gz", method(:prerelease_specs) @server.mount_proc "/quick/", method(:quick) @server.mount_proc("/gem-server-rdoc-style.css") do |req, res| res['content-type'] = 'text/css' add_date res res.body << RDOC_CSS end @server.mount_proc "/", method(:root) @server.mount_proc "/rdoc", method(:rdoc) file_handlers = { '/gems' => '/cache/', } if have_rdoc_4_plus? @server.mount '/doc_root', RDoc::Servlet, '/doc_root' else file_handlers['/doc_root'] = '/doc/' end @gem_dirs.each do |gem_dir| file_handlers.each do |mount_point, mount_dir| @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler, File.join(gem_dir, mount_dir), true) end end trap("INT") { @server.shutdown; exit! } trap("TERM") { @server.shutdown; exit! } launch if @launch @server.start end def specs(req, res) reset_gems add_date res specs = Gem::Specification.sort_by(&:sort_obj).map do |spec| platform = spec.original_platform || Gem::Platform::RUBY [spec.name, spec.version, platform] end specs = Marshal.dump specs if req.path =~ /\.gz$/ specs = Gem::Util.gzip specs res['content-type'] = 'application/x-gzip' else res['content-type'] = 'application/octet-stream' end if req.request_method == 'HEAD' res['content-length'] = specs.length else res.body << specs end end def launch listeners = @server.listeners.map{|l| l.addr[2] } # TODO: 0.0.0.0 == any, not localhost. host = listeners.any?{|l| l == '0.0.0.0' } ? 'localhost' : listeners.first say "Launching browser to http://#{host}:#{@port}" system("#{@launch} http://#{host}:#{@port}") end end rubygems/rubygems/query_utils.rb 0000644 00000021157 15102661023 0013152 0 ustar 00 # frozen_string_literal: true require_relative 'local_remote_options' require_relative 'spec_fetcher' require_relative 'version_option' require_relative 'text' module Gem::QueryUtils include Gem::Text include Gem::LocalRemoteOptions include Gem::VersionOption def add_query_options add_option('-i', '--[no-]installed', 'Check for installed gem') do |value, options| options[:installed] = value end add_option('-I', 'Equivalent to --no-installed') do |value, options| options[:installed] = false end add_version_option command, "for use with --installed" add_option('-d', '--[no-]details', 'Display detailed information of gem(s)') do |value, options| options[:details] = value end add_option('--[no-]versions', 'Display only gem names') do |value, options| options[:versions] = value options[:details] = false unless value end add_option('-a', '--all', 'Display all gem versions') do |value, options| options[:all] = value end add_option('-e', '--exact', 'Name of gem(s) to query on matches the', 'provided STRING') do |value, options| options[:exact] = value end add_option('--[no-]prerelease', 'Display prerelease versions') do |value, options| options[:prerelease] = value end add_local_remote_options end def defaults_str # :nodoc: "--local --name-matches // --no-details --versions --no-installed" end def execute gem_names = Array(options[:name]) if !args.empty? gem_names = options[:exact] ? args.map{|arg| /\A#{Regexp.escape(arg)}\Z/ } : args.map{|arg| /#{arg}/i } end terminate_interaction(check_installed_gems(gem_names)) if check_installed_gems? gem_names.each {|n| show_gems(n) } end private def check_installed_gems(gem_names) exit_code = 0 if args.empty? && !gem_name? alert_error "You must specify a gem name" exit_code = 4 elsif gem_names.count > 1 alert_error "You must specify only ONE gem!" exit_code = 4 else installed = installed?(gem_names.first, options[:version]) installed = !installed unless options[:installed] say(installed) exit_code = 1 if !installed end exit_code end def check_installed_gems? !options[:installed].nil? end def gem_name? !options[:name].source.empty? end def prerelease options[:prerelease] end def show_prereleases? prerelease.nil? || prerelease end def args options[:args].to_a end def display_header(type) if (ui.outs.tty? and Gem.configuration.verbose) or both? say say "*** #{type} GEMS ***" say end end #Guts of original execute def show_gems(name) show_local_gems(name) if local? show_remote_gems(name) if remote? end def show_local_gems(name, req = Gem::Requirement.default) display_header("LOCAL") specs = Gem::Specification.find_all do |s| s.name =~ name and req =~ s.version end dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req } specs.select! do |s| dep.match?(s.name, s.version, show_prereleases?) end spec_tuples = specs.map do |spec| [spec.name_tuple, spec] end output_query_results(spec_tuples) end def show_remote_gems(name) display_header("REMOTE") fetcher = Gem::SpecFetcher.fetcher spec_tuples = if name.respond_to?(:source) && name.source.empty? fetcher.detect(specs_type) { true } else fetcher.detect(specs_type) do |name_tuple| name === name_tuple.name end end output_query_results(spec_tuples) end def specs_type if options[:all] if options[:prerelease] :complete else :released end elsif options[:prerelease] :prerelease else :latest end end ## # Check if gem +name+ version +version+ is installed. def installed?(name, req = Gem::Requirement.default) Gem::Specification.any? {|s| s.name =~ name and req =~ s.version } end def output_query_results(spec_tuples) output = [] versions = Hash.new {|h,name| h[name] = [] } spec_tuples.each do |spec_tuple, source| versions[spec_tuple.name] << [spec_tuple, source] end versions = versions.sort_by do |(n,_),_| n.downcase end output_versions output, versions say output.join(options[:details] ? "\n\n" : "\n") end def output_versions(output, versions) versions.each do |gem_name, matching_tuples| matching_tuples = matching_tuples.sort_by {|n,_| n.version }.reverse platforms = Hash.new {|h,version| h[version] = [] } matching_tuples.each do |n, _| platforms[n.version] << n.platform if n.platform end seen = {} matching_tuples.delete_if do |n,_| if seen[n.version] true else seen[n.version] = true false end end output << clean_text(make_entry(matching_tuples, platforms)) end end def entry_details(entry, detail_tuple, specs, platforms) return unless options[:details] name_tuple, spec = detail_tuple spec = spec.fetch_spec(name_tuple)if spec.respond_to?(:fetch_spec) entry << "\n" spec_platforms entry, platforms spec_authors entry, spec spec_homepage entry, spec spec_license entry, spec spec_loaded_from entry, spec, specs spec_summary entry, spec end def entry_versions(entry, name_tuples, platforms, specs) return unless options[:versions] list = if platforms.empty? or options[:details] name_tuples.map {|n| n.version }.uniq else platforms.sort.reverse.map do |version, pls| out = version.to_s if options[:domain] == :local default = specs.any? do |s| !s.is_a?(Gem::Source) && s.version == version && s.default_gem? end out = "default: #{out}" if default end if pls != [Gem::Platform::RUBY] platform_list = [pls.delete(Gem::Platform::RUBY), *pls.sort].compact out = platform_list.unshift(out).join(' ') end out end end entry << " (#{list.join ', '})" end def make_entry(entry_tuples, platforms) detail_tuple = entry_tuples.first name_tuples, specs = entry_tuples.flatten.partition do |item| Gem::NameTuple === item end entry = [name_tuples.first.name] entry_versions(entry, name_tuples, platforms, specs) entry_details(entry, detail_tuple, specs, platforms) entry.join end def spec_authors(entry, spec) authors = "Author#{spec.authors.length > 1 ? 's' : ''}: ".dup authors << spec.authors.join(', ') entry << format_text(authors, 68, 4) end def spec_homepage(entry, spec) return if spec.homepage.nil? or spec.homepage.empty? entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4) end def spec_license(entry, spec) return if spec.license.nil? or spec.license.empty? licenses = "License#{spec.licenses.length > 1 ? 's' : ''}: ".dup licenses << spec.licenses.join(', ') entry << "\n" << format_text(licenses, 68, 4) end def spec_loaded_from(entry, spec, specs) return unless spec.loaded_from if specs.length == 1 default = spec.default_gem? ? ' (default)' : nil entry << "\n" << " Installed at#{default}: #{spec.base_dir}" else label = 'Installed at' specs.each do |s| version = s.version.to_s version << ', default' if s.default_gem? entry << "\n" << " #{label} (#{version}): #{s.base_dir}" label = ' ' * label.length end end end def spec_platforms(entry, platforms) non_ruby = platforms.any? do |_, pls| pls.any? {|pl| pl != Gem::Platform::RUBY } end return unless non_ruby if platforms.length == 1 title = platforms.values.length == 1 ? 'Platform' : 'Platforms' entry << " #{title}: #{platforms.values.sort.join(', ')}\n" else entry << " Platforms:\n" sorted_platforms = platforms.sort_by {|version,| version } sorted_platforms.each do |version, pls| label = " #{version}: " data = format_text pls.sort.join(', '), 68, label.length data[0, label.length] = label entry << data << "\n" end end end def spec_summary(entry, spec) summary = truncate_text(spec.summary, "the summary for #{spec.full_name}") entry << "\n\n" << format_text(summary, 68, 4) end end rubygems/rubygems/request_set/lockfile.rb 0000644 00000012777 15102661023 0014730 0 ustar 00 # frozen_string_literal: true ## # Parses a gem.deps.rb.lock file and constructs a LockSet containing the # dependencies found inside. If the lock file is missing no LockSet is # constructed. class Gem::RequestSet::Lockfile ## # Raised when a lockfile cannot be parsed class ParseError < Gem::Exception ## # The column where the error was encountered attr_reader :column ## # The line where the error was encountered attr_reader :line ## # The location of the lock file attr_reader :path ## # Raises a ParseError with the given +message+ which was encountered at a # +line+ and +column+ while parsing. def initialize(message, column, line, path) @line = line @column = column @path = path super "#{message} (at line #{line} column #{column})" end end ## # Creates a new Lockfile for the given +request_set+ and +gem_deps_file+ # location. def self.build(request_set, gem_deps_file, dependencies = nil) request_set.resolve dependencies ||= requests_to_deps request_set.sorted_requests new request_set, gem_deps_file, dependencies end def self.requests_to_deps(requests) # :nodoc: deps = {} requests.each do |request| spec = request.spec name = request.name requirement = request.request.dependency.requirement deps[name] = if [Gem::Resolver::VendorSpecification, Gem::Resolver::GitSpecification].include? spec.class Gem::Requirement.source_set else requirement end end deps end ## # The platforms for this Lockfile attr_reader :platforms def initialize(request_set, gem_deps_file, dependencies) @set = request_set @dependencies = dependencies @gem_deps_file = File.expand_path(gem_deps_file) @gem_deps_dir = File.dirname(@gem_deps_file) if RUBY_VERSION < '2.7' @gem_deps_file.untaint unless gem_deps_file.tainted? end @platforms = [] end def add_DEPENDENCIES(out) # :nodoc: out << "DEPENDENCIES" out.concat @dependencies.sort_by {|name,| name }.map {|name, requirement| " #{name}#{requirement.for_lockfile}" } out << nil end def add_GEM(out, spec_groups) # :nodoc: return if spec_groups.empty? source_groups = spec_groups.values.flatten.group_by do |request| request.spec.source.uri end source_groups.sort_by {|group,| group.to_s }.map do |group, requests| out << "GEM" out << " remote: #{group}" out << " specs:" requests.sort_by {|request| request.name }.each do |request| next if request.spec.name == 'bundler' platform = "-#{request.spec.platform}" unless Gem::Platform::RUBY == request.spec.platform out << " #{request.name} (#{request.version}#{platform})" request.full_spec.dependencies.sort.each do |dependency| next if dependency.type == :development requirement = dependency.requirement out << " #{dependency.name}#{requirement.for_lockfile}" end end out << nil end end def add_GIT(out, git_requests) return if git_requests.empty? by_repository_revision = git_requests.group_by do |request| source = request.spec.source [source.repository, source.rev_parse] end by_repository_revision.each do |(repository, revision), requests| out << "GIT" out << " remote: #{repository}" out << " revision: #{revision}" out << " specs:" requests.sort_by {|request| request.name }.each do |request| out << " #{request.name} (#{request.version})" dependencies = request.spec.dependencies.sort_by {|dep| dep.name } dependencies.each do |dep| out << " #{dep.name}#{dep.requirement.for_lockfile}" end end out << nil end end def relative_path_from(dest, base) # :nodoc: dest = File.expand_path(dest) base = File.expand_path(base) if dest.index(base) == 0 offset = dest[base.size + 1..-1] return '.' unless offset offset else dest end end def add_PATH(out, path_requests) # :nodoc: return if path_requests.empty? out << "PATH" path_requests.each do |request| directory = File.expand_path(request.spec.source.uri) out << " remote: #{relative_path_from directory, @gem_deps_dir}" out << " specs:" out << " #{request.name} (#{request.version})" end out << nil end def add_PLATFORMS(out) # :nodoc: out << "PLATFORMS" platforms = requests.map {|request| request.spec.platform }.uniq platforms = platforms.sort_by {|platform| platform.to_s } platforms.each do |platform| out << " #{platform}" end out << nil end def spec_groups requests.group_by {|request| request.spec.class } end ## # The contents of the lock file. def to_s out = [] groups = spec_groups add_PATH out, groups.delete(Gem::Resolver::VendorSpecification) { [] } add_GIT out, groups.delete(Gem::Resolver::GitSpecification) { [] } add_GEM out, groups add_PLATFORMS out add_DEPENDENCIES out out.join "\n" end ## # Writes the lock file alongside the gem dependencies file def write content = to_s File.open "#{@gem_deps_file}.lock", 'w' do |io| io.write content end end private def requests @set.sorted_requests end end require_relative 'lockfile/tokenizer' rubygems/rubygems/request_set/lockfile/tokenizer.rb 0000644 00000005401 15102661023 0016724 0 ustar 00 # frozen_string_literal: true require_relative 'parser' class Gem::RequestSet::Lockfile::Tokenizer Token = Struct.new :type, :value, :column, :line EOF = Token.new :EOF def self.from_file(file) new File.read(file), file end def initialize(input, filename = nil, line = 0, pos = 0) @line = line @line_pos = pos @tokens = [] @filename = filename tokenize input end def make_parser(set, platforms) Gem::RequestSet::Lockfile::Parser.new self, set, platforms, @filename end def to_a @tokens.map {|token| [token.type, token.value, token.column, token.line] } end def skip(type) @tokens.shift while not @tokens.empty? and peek.type == type end ## # Calculates the column (by byte) and the line of the current token based on # +byte_offset+. def token_pos(byte_offset) # :nodoc: [byte_offset - @line_pos, @line] end def empty? @tokens.empty? end def unshift(token) @tokens.unshift token end def next_token @tokens.shift end alias :shift :next_token def peek @tokens.first || EOF end private def tokenize(input) require 'strscan' s = StringScanner.new input until s.eos? do pos = s.pos pos = s.pos if leading_whitespace = s.scan(/ +/) if s.scan(/[<|=>]{7}/) message = "your #{@filename} contains merge conflict markers" column, line = token_pos pos raise Gem::RequestSet::Lockfile::ParseError.new message, column, line, @filename end @tokens << case when s.scan(/\r?\n/) then token = Token.new(:newline, nil, *token_pos(pos)) @line_pos = s.pos @line += 1 token when s.scan(/[A-Z]+/) then if leading_whitespace text = s.matched text += s.scan(/[^\s)]*/).to_s # in case of no match Token.new(:text, text, *token_pos(pos)) else Token.new(:section, s.matched, *token_pos(pos)) end when s.scan(/([a-z]+):\s/) then s.pos -= 1 # rewind for possible newline Token.new(:entry, s[1], *token_pos(pos)) when s.scan(/\(/) then Token.new(:l_paren, nil, *token_pos(pos)) when s.scan(/\)/) then Token.new(:r_paren, nil, *token_pos(pos)) when s.scan(/<=|>=|=|~>|<|>|!=/) then Token.new(:requirement, s.matched, *token_pos(pos)) when s.scan(/,/) then Token.new(:comma, nil, *token_pos(pos)) when s.scan(/!/) then Token.new(:bang, nil, *token_pos(pos)) when s.scan(/[^\s),!]*/) then Token.new(:text, s.matched, *token_pos(pos)) else raise "BUG: can't create token for: #{s.string[s.pos..-1].inspect}" end end @tokens end end rubygems/rubygems/request_set/lockfile/parser.rb 0000644 00000016442 15102661023 0016215 0 ustar 00 # frozen_string_literal: true class Gem::RequestSet::Lockfile::Parser ### # Parses lockfiles def initialize(tokenizer, set, platforms, filename = nil) @tokens = tokenizer @filename = filename @set = set @platforms = platforms end def parse until @tokens.empty? do token = get case token.type when :section then @tokens.skip :newline case token.value when 'DEPENDENCIES' then parse_DEPENDENCIES when 'GIT' then parse_GIT when 'GEM' then parse_GEM when 'PATH' then parse_PATH when 'PLATFORMS' then parse_PLATFORMS else token = get until @tokens.empty? or peek.first == :section end else raise "BUG: unhandled token #{token.type} (#{token.value.inspect}) at line #{token.line} column #{token.column}" end end end ## # Gets the next token for a Lockfile def get(expected_types = nil, expected_value = nil) # :nodoc: token = @tokens.shift if expected_types and not Array(expected_types).include? token.type unget token message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " + "expected #{expected_types.inspect}" raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename end if expected_value and expected_value != token.value unget token message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " + "expected [#{expected_types.inspect}, " + "#{expected_value.inspect}]" raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename end token end def parse_DEPENDENCIES # :nodoc: while not @tokens.empty? and :text == peek.type do token = get :text requirements = [] case peek[0] when :bang then get :bang requirements << pinned_requirement(token.value) when :l_paren then get :l_paren loop do op = get(:requirement).value version = get(:text).value requirements << "#{op} #{version}" break unless peek.type == :comma get :comma end get :r_paren if peek[0] == :bang requirements.clear requirements << pinned_requirement(token.value) get :bang end end @set.gem token.value, *requirements skip :newline end end def parse_GEM # :nodoc: sources = [] while [:entry, 'remote'] == peek.first(2) do get :entry, 'remote' data = get(:text).value skip :newline sources << Gem::Source.new(data) end sources << Gem::Source.new(Gem::DEFAULT_HOST) if sources.empty? get :entry, 'specs' skip :newline set = Gem::Resolver::LockSet.new sources last_specs = nil while not @tokens.empty? and :text == peek.type do token = get :text name = token.value column = token.column case peek[0] when :newline then last_specs.each do |spec| spec.add_dependency Gem::Dependency.new name if column == 6 end when :l_paren then get :l_paren token = get [:text, :requirement] type = token.type data = token.value if type == :text and column == 4 version, platform = data.split '-', 2 platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY last_specs = set.add name, version, platform else dependency = parse_dependency name, data last_specs.each do |spec| spec.add_dependency dependency end end get :r_paren else raise "BUG: unknown token #{peek}" end skip :newline end @set.sets << set end def parse_GIT # :nodoc: get :entry, 'remote' repository = get(:text).value skip :newline get :entry, 'revision' revision = get(:text).value skip :newline type = peek.type value = peek.value if type == :entry and %w[branch ref tag].include? value get get :text skip :newline end get :entry, 'specs' skip :newline set = Gem::Resolver::GitSet.new set.root_dir = @set.install_dir last_spec = nil while not @tokens.empty? and :text == peek.type do token = get :text name = token.value column = token.column case peek[0] when :newline then last_spec.add_dependency Gem::Dependency.new name if column == 6 when :l_paren then get :l_paren token = get [:text, :requirement] type = token.type data = token.value if type == :text and column == 4 last_spec = set.add_git_spec name, data, repository, revision, true else dependency = parse_dependency name, data last_spec.add_dependency dependency end get :r_paren else raise "BUG: unknown token #{peek}" end skip :newline end @set.sets << set end def parse_PATH # :nodoc: get :entry, 'remote' directory = get(:text).value skip :newline get :entry, 'specs' skip :newline set = Gem::Resolver::VendorSet.new last_spec = nil while not @tokens.empty? and :text == peek.first do token = get :text name = token.value column = token.column case peek[0] when :newline then last_spec.add_dependency Gem::Dependency.new name if column == 6 when :l_paren then get :l_paren token = get [:text, :requirement] type = token.type data = token.value if type == :text and column == 4 last_spec = set.add_vendor_gem name, directory else dependency = parse_dependency name, data last_spec.dependencies << dependency end get :r_paren else raise "BUG: unknown token #{peek}" end skip :newline end @set.sets << set end def parse_PLATFORMS # :nodoc: while not @tokens.empty? and :text == peek.first do name = get(:text).value @platforms << name skip :newline end end ## # Parses the requirements following the dependency +name+ and the +op+ for # the first token of the requirements and returns a Gem::Dependency object. def parse_dependency(name, op) # :nodoc: return Gem::Dependency.new name, op unless peek[0] == :text version = get(:text).value requirements = ["#{op} #{version}"] while peek.type == :comma do get :comma op = get(:requirement).value version = get(:text).value requirements << "#{op} #{version}" end Gem::Dependency.new name, requirements end private def skip(type) # :nodoc: @tokens.skip type end ## # Peeks at the next token for Lockfile def peek # :nodoc: @tokens.peek end def pinned_requirement(name) # :nodoc: requirement = Gem::Dependency.new name specification = @set.sets.flat_map do |set| set.find_all(requirement) end.compact.first specification && specification.version end ## # Ungets the last token retrieved by #get def unget(token) # :nodoc: @tokens.unshift token end end rubygems/rubygems/request_set/gem_dependency_api.rb 0000644 00000055131 15102661023 0016726 0 ustar 00 # frozen_string_literal: true ## # A semi-compatible DSL for the Bundler Gemfile and Isolate gem dependencies # files. # # To work with both the Bundler Gemfile and Isolate formats this # implementation takes some liberties to allow compatibility with each, most # notably in #source. # # A basic gem dependencies file will look like the following: # # source 'https://rubygems.org' # # gem 'rails', '3.2.14a # gem 'devise', '~> 2.1', '>= 2.1.3' # gem 'cancan' # gem 'airbrake' # gem 'pg' # # RubyGems recommends saving this as gem.deps.rb over Gemfile or Isolate. # # To install the gems in this Gemfile use `gem install -g` to install it and # create a lockfile. The lockfile will ensure that when you make changes to # your gem dependencies file a minimum amount of change is made to the # dependencies of your gems. # # RubyGems can activate all the gems in your dependencies file at startup # using the RUBYGEMS_GEMDEPS environment variable or through Gem.use_gemdeps. # See Gem.use_gemdeps for details and warnings. # # See `gem help install` and `gem help gem_dependencies` for further details. class Gem::RequestSet::GemDependencyAPI ENGINE_MAP = { # :nodoc: :jruby => %w[jruby], :jruby_18 => %w[jruby], :jruby_19 => %w[jruby], :maglev => %w[maglev], :mri => %w[ruby], :mri_18 => %w[ruby], :mri_19 => %w[ruby], :mri_20 => %w[ruby], :mri_21 => %w[ruby], :rbx => %w[rbx], :truffleruby => %w[truffleruby], :ruby => %w[ruby rbx maglev truffleruby], :ruby_18 => %w[ruby rbx maglev truffleruby], :ruby_19 => %w[ruby rbx maglev truffleruby], :ruby_20 => %w[ruby rbx maglev truffleruby], :ruby_21 => %w[ruby rbx maglev truffleruby], }.freeze mswin = Gem::Platform.new 'x86-mswin32' mswin64 = Gem::Platform.new 'x64-mswin64' x86_mingw = Gem::Platform.new 'x86-mingw32' x64_mingw = Gem::Platform.new 'x64-mingw32' PLATFORM_MAP = { # :nodoc: :jruby => Gem::Platform::RUBY, :jruby_18 => Gem::Platform::RUBY, :jruby_19 => Gem::Platform::RUBY, :maglev => Gem::Platform::RUBY, :mingw => x86_mingw, :mingw_18 => x86_mingw, :mingw_19 => x86_mingw, :mingw_20 => x86_mingw, :mingw_21 => x86_mingw, :mri => Gem::Platform::RUBY, :mri_18 => Gem::Platform::RUBY, :mri_19 => Gem::Platform::RUBY, :mri_20 => Gem::Platform::RUBY, :mri_21 => Gem::Platform::RUBY, :mswin => mswin, :mswin_18 => mswin, :mswin_19 => mswin, :mswin_20 => mswin, :mswin_21 => mswin, :mswin64 => mswin64, :mswin64_19 => mswin64, :mswin64_20 => mswin64, :mswin64_21 => mswin64, :rbx => Gem::Platform::RUBY, :ruby => Gem::Platform::RUBY, :ruby_18 => Gem::Platform::RUBY, :ruby_19 => Gem::Platform::RUBY, :ruby_20 => Gem::Platform::RUBY, :ruby_21 => Gem::Platform::RUBY, :truffleruby => Gem::Platform::RUBY, :x64_mingw => x64_mingw, :x64_mingw_20 => x64_mingw, :x64_mingw_21 => x64_mingw, }.freeze gt_eq_0 = Gem::Requirement.new '>= 0' tilde_gt_1_8_0 = Gem::Requirement.new '~> 1.8.0' tilde_gt_1_9_0 = Gem::Requirement.new '~> 1.9.0' tilde_gt_2_0_0 = Gem::Requirement.new '~> 2.0.0' tilde_gt_2_1_0 = Gem::Requirement.new '~> 2.1.0' VERSION_MAP = { # :nodoc: :jruby => gt_eq_0, :jruby_18 => tilde_gt_1_8_0, :jruby_19 => tilde_gt_1_9_0, :maglev => gt_eq_0, :mingw => gt_eq_0, :mingw_18 => tilde_gt_1_8_0, :mingw_19 => tilde_gt_1_9_0, :mingw_20 => tilde_gt_2_0_0, :mingw_21 => tilde_gt_2_1_0, :mri => gt_eq_0, :mri_18 => tilde_gt_1_8_0, :mri_19 => tilde_gt_1_9_0, :mri_20 => tilde_gt_2_0_0, :mri_21 => tilde_gt_2_1_0, :mswin => gt_eq_0, :mswin_18 => tilde_gt_1_8_0, :mswin_19 => tilde_gt_1_9_0, :mswin_20 => tilde_gt_2_0_0, :mswin_21 => tilde_gt_2_1_0, :mswin64 => gt_eq_0, :mswin64_19 => tilde_gt_1_9_0, :mswin64_20 => tilde_gt_2_0_0, :mswin64_21 => tilde_gt_2_1_0, :rbx => gt_eq_0, :ruby => gt_eq_0, :ruby_18 => tilde_gt_1_8_0, :ruby_19 => tilde_gt_1_9_0, :ruby_20 => tilde_gt_2_0_0, :ruby_21 => tilde_gt_2_1_0, :truffleruby => gt_eq_0, :x64_mingw => gt_eq_0, :x64_mingw_20 => tilde_gt_2_0_0, :x64_mingw_21 => tilde_gt_2_1_0, }.freeze WINDOWS = { # :nodoc: :mingw => :only, :mingw_18 => :only, :mingw_19 => :only, :mingw_20 => :only, :mingw_21 => :only, :mri => :never, :mri_18 => :never, :mri_19 => :never, :mri_20 => :never, :mri_21 => :never, :mswin => :only, :mswin_18 => :only, :mswin_19 => :only, :mswin_20 => :only, :mswin_21 => :only, :mswin64 => :only, :mswin64_19 => :only, :mswin64_20 => :only, :mswin64_21 => :only, :rbx => :never, :ruby => :never, :ruby_18 => :never, :ruby_19 => :never, :ruby_20 => :never, :ruby_21 => :never, :x64_mingw => :only, :x64_mingw_20 => :only, :x64_mingw_21 => :only, }.freeze ## # The gems required by #gem statements in the gem.deps.rb file attr_reader :dependencies ## # A set of gems that are loaded via the +:git+ option to #gem attr_reader :git_set # :nodoc: ## # A Hash containing gem names and files to require from those gems. attr_reader :requires ## # A set of gems that are loaded via the +:path+ option to #gem attr_reader :vendor_set # :nodoc: ## # The groups of gems to exclude from installation attr_accessor :without_groups # :nodoc: ## # Creates a new GemDependencyAPI that will add dependencies to the # Gem::RequestSet +set+ based on the dependency API description in +path+. def initialize(set, path) @set = set @path = path @current_groups = nil @current_platforms = nil @current_repository = nil @dependencies = {} @default_sources = true @git_set = @set.git_set @git_sources = {} @installing = false @requires = Hash.new {|h, name| h[name] = [] } @vendor_set = @set.vendor_set @source_set = @set.source_set @gem_sources = {} @without_groups = [] git_source :github do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/" "git://github.com/#{repo_name}.git" end git_source :bitbucket do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/" user, = repo_name.split "/", 2 "https://#{user}@bitbucket.org/#{repo_name}.git" end end ## # Adds +dependencies+ to the request set if any of the +groups+ are allowed. # This is used for gemspec dependencies. def add_dependencies(groups, dependencies) # :nodoc: return unless (groups & @without_groups).empty? dependencies.each do |dep| @set.gem dep.name, *dep.requirement.as_list end end private :add_dependencies ## # Finds a gemspec with the given +name+ that lives at +path+. def find_gemspec(name, path) # :nodoc: glob = File.join path, "#{name}.gemspec" spec_files = Dir[glob] case spec_files.length when 1 then spec_file = spec_files.first spec = Gem::Specification.load spec_file return spec if spec raise ArgumentError, "invalid gemspec #{spec_file}" when 0 then raise ArgumentError, "no gemspecs found at #{Dir.pwd}" else raise ArgumentError, "found multiple gemspecs at #{Dir.pwd}, " + "use the name: option to specify the one you want" end end ## # Changes the behavior of gem dependency file loading to installing mode. # In installing mode certain restrictions are ignored such as ruby version # mismatch checks. def installing=(installing) # :nodoc: @installing = installing end ## # Loads the gem dependency file and returns self. def load instance_eval File.read(@path).tap(&Gem::UNTAINT), @path, 1 self end ## # :category: Gem Dependencies DSL # # :call-seq: # gem(name) # gem(name, *requirements) # gem(name, *requirements, options) # # Specifies a gem dependency with the given +name+ and +requirements+. You # may also supply +options+ following the +requirements+ # # +options+ include: # # require: :: # RubyGems does not provide any autorequire features so requires in a gem # dependencies file are recorded but ignored. # # In bundler the require: option overrides the file to require during # Bundler.require. By default the name of the dependency is required in # Bundler. A single file or an Array of files may be given. # # To disable requiring any file give +false+: # # gem 'rake', require: false # # group: :: # Place the dependencies in the given dependency group. A single group or # an Array of groups may be given. # # See also #group # # platform: :: # Only install the dependency on the given platform. A single platform or # an Array of platforms may be given. # # See #platform for a list of platforms available. # # path: :: # Install this dependency from an unpacked gem in the given directory. # # gem 'modified_gem', path: 'vendor/modified_gem' # # git: :: # Install this dependency from a git repository: # # gem 'private_gem', git: git@my.company.example:private_gem.git' # # gist: :: # Install this dependency from the gist ID: # # gem 'bang', gist: '1232884' # # github: :: # Install this dependency from a github git repository: # # gem 'private_gem', github: 'my_company/private_gem' # # submodules: :: # Set to +true+ to include submodules when fetching the git repository for # git:, gist: and github: dependencies. # # ref: :: # Use the given commit name or SHA for git:, gist: and github: # dependencies. # # branch: :: # Use the given branch for git:, gist: and github: dependencies. # # tag: :: # Use the given tag for git:, gist: and github: dependencies. def gem(name, *requirements) options = requirements.pop if requirements.last.kind_of?(Hash) options ||= {} options[:git] = @current_repository if @current_repository source_set = false source_set ||= gem_path name, options source_set ||= gem_git name, options source_set ||= gem_git_source name, options source_set ||= gem_source name, options duplicate = @dependencies.include? name @dependencies[name] = if requirements.empty? and not source_set Gem::Requirement.default elsif source_set Gem::Requirement.source_set else Gem::Requirement.create requirements end return unless gem_platforms name, options groups = gem_group name, options return unless (groups & @without_groups).empty? pin_gem_source name, :default unless source_set gem_requires name, options if duplicate warn <<-WARNING Gem dependencies file #{@path} requires #{name} more than once. WARNING end @set.gem name, *requirements end ## # Handles the git: option from +options+ for gem +name+. # # Returns +true+ if the gist or git option was handled. def gem_git(name, options) # :nodoc: if gist = options.delete(:gist) options[:git] = "https://gist.github.com/#{gist}.git" end return unless repository = options.delete(:git) pin_gem_source name, :git, repository reference = gem_git_reference options submodules = options.delete :submodules @git_set.add_git_gem name, repository, reference, submodules true end ## # Handles the git options from +options+ for git gem. # # Returns reference for the git gem. def gem_git_reference(options) # :nodoc: ref = options.delete :ref branch = options.delete :branch tag = options.delete :tag reference = nil reference ||= ref reference ||= branch reference ||= tag reference ||= 'master' if ref && branch warn <<-WARNING Gem dependencies file #{@path} includes git reference for both ref and branch but only ref is used. WARNING end if (ref || branch) && tag warn <<-WARNING Gem dependencies file #{@path} includes git reference for both ref/branch and tag but only ref/branch is used. WARNING end reference end private :gem_git ## # Handles a git gem option from +options+ for gem +name+ for a git source # registered through git_source. # # Returns +true+ if the custom source option was handled. def gem_git_source(name, options) # :nodoc: return unless git_source = (@git_sources.keys & options.keys).last source_callback = @git_sources[git_source] source_param = options.delete git_source git_url = source_callback.call source_param options[:git] = git_url gem_git name, options true end private :gem_git_source ## # Handles the :group and :groups +options+ for the gem with the given # +name+. def gem_group(name, options) # :nodoc: g = options.delete :group all_groups = g ? Array(g) : [] groups = options.delete :groups all_groups |= groups if groups all_groups |= @current_groups if @current_groups all_groups end private :gem_group ## # Handles the path: option from +options+ for gem +name+. # # Returns +true+ if the path option was handled. def gem_path(name, options) # :nodoc: return unless directory = options.delete(:path) pin_gem_source name, :path, directory @vendor_set.add_vendor_gem name, directory true end private :gem_path ## # Handles the source: option from +options+ for gem +name+. # # Returns +true+ if the source option was handled. def gem_source(name, options) # :nodoc: return unless source = options.delete(:source) pin_gem_source name, :source, source @source_set.add_source_gem name, source true end private :gem_source ## # Handles the platforms: option from +options+. Returns true if the # platform matches the current platform. def gem_platforms(name, options) # :nodoc: platform_names = Array(options.delete :platform) platform_names.concat Array(options.delete :platforms) platform_names.concat @current_platforms if @current_platforms return true if platform_names.empty? platform_names.any? do |platform_name| raise ArgumentError, "unknown platform #{platform_name.inspect}" unless platform = PLATFORM_MAP[platform_name] next false unless Gem::Platform.match_gem? platform, name if engines = ENGINE_MAP[platform_name] next false unless engines.include? Gem.ruby_engine end case WINDOWS[platform_name] when :only then next false unless Gem.win_platform? when :never then next false if Gem.win_platform? end VERSION_MAP[platform_name].satisfied_by? Gem.ruby_version end end private :gem_platforms ## # Records the require: option from +options+ and adds those files, or the # default file to the require list for +name+. def gem_requires(name, options) # :nodoc: if options.include? :require if requires = options.delete(:require) @requires[name].concat Array requires end else @requires[name] << name end raise ArgumentError, "Unhandled gem options #{options.inspect}" unless options.empty? end private :gem_requires ## # :category: Gem Dependencies DSL # # Block form for specifying gems from a git +repository+. # # git 'https://github.com/rails/rails.git' do # gem 'activesupport' # gem 'activerecord' # end def git(repository) @current_repository = repository yield ensure @current_repository = nil end ## # Defines a custom git source that uses +name+ to expand git repositories # for use in gems built from git repositories. You must provide a block # that accepts a git repository name for expansion. def git_source(name, &callback) @git_sources[name] = callback end ## # Returns the basename of the file the dependencies were loaded from def gem_deps_file # :nodoc: File.basename @path end ## # :category: Gem Dependencies DSL # # Loads dependencies from a gemspec file. # # +options+ include: # # name: :: # The name portion of the gemspec file. Defaults to searching for any # gemspec file in the current directory. # # gemspec name: 'my_gem' # # path: :: # The path the gemspec lives in. Defaults to the current directory: # # gemspec 'my_gem', path: 'gemspecs', name: 'my_gem' # # development_group: :: # The group to add development dependencies to. By default this is # :development. Only one group may be specified. def gemspec(options = {}) name = options.delete(:name) || '{,*}' path = options.delete(:path) || '.' development_group = options.delete(:development_group) || :development spec = find_gemspec name, path groups = gem_group spec.name, {} self_dep = Gem::Dependency.new spec.name, spec.version add_dependencies groups, [self_dep] add_dependencies groups, spec.runtime_dependencies @dependencies[spec.name] = Gem::Requirement.source_set spec.dependencies.each do |dep| @dependencies[dep.name] = dep.requirement end groups << development_group add_dependencies groups, spec.development_dependencies @vendor_set.add_vendor_gem spec.name, path gem_requires spec.name, options end ## # :category: Gem Dependencies DSL # # Block form for placing a dependency in the given +groups+. # # group :development do # gem 'debugger' # end # # group :development, :test do # gem 'minitest' # end # # Groups can be excluded at install time using `gem install -g --without # development`. See `gem help install` and `gem help gem_dependencies` for # further details. def group(*groups) @current_groups = groups yield ensure @current_groups = nil end ## # Pins the gem +name+ to the given +source+. Adding a gem with the same # name from a different +source+ will raise an exception. def pin_gem_source(name, type = :default, source = nil) source_description = case type when :default then '(default)' when :path then "path: #{source}" when :git then "git: #{source}" when :source then "source: #{source}" else '(unknown)' end raise ArgumentError, "duplicate source #{source_description} for gem #{name}" if @gem_sources.fetch(name, source) != source @gem_sources[name] = source end private :pin_gem_source ## # :category: Gem Dependencies DSL # # Block form for restricting gems to a set of platforms. # # The gem dependencies platform is different from Gem::Platform. A platform # gem.deps.rb platform matches on the ruby engine, the ruby version and # whether or not windows is allowed. # # :ruby, :ruby_XY :: # Matches non-windows, non-jruby implementations where X and Y can be used # to match releases in the 1.8, 1.9, 2.0 or 2.1 series. # # :mri, :mri_XY :: # Matches non-windows C Ruby (Matz Ruby) or only the 1.8, 1.9, 2.0 or # 2.1 series. # # :mingw, :mingw_XY :: # Matches 32 bit C Ruby on MinGW or only the 1.8, 1.9, 2.0 or 2.1 series. # # :x64_mingw, :x64_mingw_XY :: # Matches 64 bit C Ruby on MinGW or only the 1.8, 1.9, 2.0 or 2.1 series. # # :mswin, :mswin_XY :: # Matches 32 bit C Ruby on Microsoft Windows or only the 1.8, 1.9, 2.0 or # 2.1 series. # # :mswin64, :mswin64_XY :: # Matches 64 bit C Ruby on Microsoft Windows or only the 1.8, 1.9, 2.0 or # 2.1 series. # # :jruby, :jruby_XY :: # Matches JRuby or JRuby in 1.8 or 1.9 mode. # # :maglev :: # Matches Maglev # # :rbx :: # Matches non-windows Rubinius # # NOTE: There is inconsistency in what environment a platform matches. You # may need to read the source to know the exact details. def platform(*platforms) @current_platforms = platforms yield ensure @current_platforms = nil end ## # :category: Gem Dependencies DSL # # Block form for restricting gems to a particular set of platforms. See # #platform. alias :platforms :platform ## # :category: Gem Dependencies DSL # # Restricts this gem dependencies file to the given ruby +version+. # # You may also provide +engine:+ and +engine_version:+ options to restrict # this gem dependencies file to a particular ruby engine and its engine # version. This matching is performed by using the RUBY_ENGINE and # RUBY_ENGINE_VERSION constants. def ruby(version, options = {}) engine = options[:engine] engine_version = options[:engine_version] raise ArgumentError, 'You must specify engine_version along with the Ruby engine' if engine and not engine_version return true if @installing unless RUBY_VERSION == version message = "Your Ruby version is #{RUBY_VERSION}, " + "but your #{gem_deps_file} requires #{version}" raise Gem::RubyVersionMismatch, message end if engine and engine != Gem.ruby_engine message = "Your Ruby engine is #{Gem.ruby_engine}, " + "but your #{gem_deps_file} requires #{engine}" raise Gem::RubyVersionMismatch, message end if engine_version if engine_version != RUBY_ENGINE_VERSION message = "Your Ruby engine version is #{Gem.ruby_engine} #{RUBY_ENGINE_VERSION}, " + "but your #{gem_deps_file} requires #{engine} #{engine_version}" raise Gem::RubyVersionMismatch, message end end return true end ## # :category: Gem Dependencies DSL # # Sets +url+ as a source for gems for this dependency API. RubyGems uses # the default configured sources if no source was given. If a source is set # only that source is used. # # This method differs in behavior from Bundler: # # * The +:gemcutter+, # +:rubygems+ and +:rubyforge+ sources are not # supported as they are deprecated in bundler. # * The +prepend:+ option is not supported. If you wish to order sources # then list them in your preferred order. def source(url) Gem.sources.clear if @default_sources @default_sources = false Gem.sources << url end end rubygems/rubygems/bundler_version_finder.rb 0000644 00000006052 15102661023 0015311 0 ustar 00 # frozen_string_literal: true module Gem::BundlerVersionFinder def self.bundler_version version, _ = bundler_version_with_reason return unless version Gem::Version.new(version) end def self.bundler_version_with_reason if v = ENV["BUNDLER_VERSION"] return [v, "`$BUNDLER_VERSION`"] end if v = bundle_update_bundler_version return if v == true return [v, "`bundle update --bundler`"] end v, lockfile = lockfile_version if v return [v, "your #{lockfile}"] end end def self.missing_version_message return unless vr = bundler_version_with_reason <<-EOS Could not find 'bundler' (#{vr.first}) required by #{vr.last}. To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:#{vr.first}` EOS end def self.compatible?(spec) return true unless spec.name == "bundler".freeze return true unless bundler_version = self.bundler_version spec.version.segments.first == bundler_version.segments.first end def self.filter!(specs) return unless bundler_version = self.bundler_version specs.reject! {|spec| spec.version.segments.first != bundler_version.segments.first } exact_match_index = specs.find_index {|spec| spec.version == bundler_version } return unless exact_match_index specs.unshift(specs.delete_at(exact_match_index)) end def self.bundle_update_bundler_version return unless File.basename($0) == "bundle".freeze return unless "update".start_with?(ARGV.first || " ") bundler_version = nil update_index = nil ARGV.each_with_index do |a, i| if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN bundler_version = a end next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ bundler_version = $1 || true update_index = i end bundler_version end private_class_method :bundle_update_bundler_version def self.lockfile_version return unless lockfile = lockfile_contents lockfile, contents = lockfile lockfile ||= "lockfile" regexp = /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ return unless contents =~ regexp [$1, lockfile] end private_class_method :lockfile_version def self.lockfile_contents gemfile = ENV["BUNDLE_GEMFILE"] gemfile = nil if gemfile && gemfile.empty? unless gemfile begin Gem::Util.traverse_parents(Dir.pwd) do |directory| next unless gemfile = Gem::GEM_DEP_FILES.find {|f| File.file?(f.tap(&Gem::UNTAINT)) } gemfile = File.join directory, gemfile break end rescue Errno::ENOENT return end end return unless gemfile lockfile = case gemfile when "gems.rb" then "gems.locked" else "#{gemfile}.lock" end.dup.tap(&Gem::UNTAINT) return unless File.file?(lockfile) [lockfile, File.read(lockfile)] end private_class_method :lockfile_contents end rubygems/rubygems/installer.rb 0000644 00000064633 15102661023 0012570 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'installer_uninstaller_utils' require_relative 'exceptions' require_relative 'deprecate' require_relative 'package' require_relative 'ext' require_relative 'user_interaction' ## # The installer installs the files contained in the .gem into the Gem.home. # # Gem::Installer does the work of putting files in all the right places on the # filesystem including unpacking the gem into its gem dir, installing the # gemspec in the specifications dir, storing the cached gem in the cache dir, # and installing either wrappers or symlinks for executables. # # The installer invokes pre and post install hooks. Hooks can be added either # through a rubygems_plugin.rb file in an installed gem or via a # rubygems/defaults/#{RUBY_ENGINE}.rb or rubygems/defaults/operating_system.rb # file. See Gem.pre_install and Gem.post_install for details. class Gem::Installer extend Gem::Deprecate ## # Paths where env(1) might live. Some systems are broken and have it in # /bin ENV_PATHS = %w[/usr/bin/env /bin/env].freeze ## # Deprecated in favor of Gem::Ext::BuildError ExtensionBuildError = Gem::Ext::BuildError # :nodoc: include Gem::UserInteraction include Gem::InstallerUninstallerUtils ## # The directory a gem's executables will be installed into attr_reader :bin_dir attr_reader :build_root # :nodoc: ## # The gem repository the gem will be installed into attr_reader :gem_home ## # The options passed when the Gem::Installer was instantiated. attr_reader :options ## # The gem package instance. attr_reader :package @path_warning = false class << self # # Changes in rubygems to lazily loading `rubygems/command` (in order to # lazily load `optparse` as a side effect) affect bundler's custom installer # which uses `Gem::Command` without requiring it (up until bundler 2.2.29). # This hook is to compensate for that missing require. # # TODO: Remove when rubygems no longer supports running on bundler older # than 2.2.29. def inherited(klass) if klass.name == "Bundler::RubyGemsGemInstaller" require "rubygems/command" end super(klass) end ## # True if we've warned about PATH not including Gem.bindir attr_accessor :path_warning ## # Overrides the executable format. # # This is a sprintf format with a "%s" which will be replaced with the # executable name. It is based off the ruby executable name's difference # from "ruby". attr_writer :exec_format # Defaults to use Ruby's program prefix and suffix. def exec_format @exec_format ||= Gem.default_exec_format end end ## # Construct an installer object for the gem file located at +path+ def self.at(path, options = {}) security_policy = options[:security_policy] package = Gem::Package.new path, security_policy new package, options end class FakePackage attr_accessor :spec attr_accessor :dir_mode attr_accessor :prog_mode attr_accessor :data_mode def initialize(spec) @spec = spec end def extract_files(destination_dir, pattern = '*') FileUtils.mkdir_p destination_dir spec.files.each do |file| file = File.join destination_dir, file next if File.exist? file FileUtils.mkdir_p File.dirname(file) File.open file, 'w' do |fp| fp.puts "# #{file}" end end end def copy_to(path) end end ## # Construct an installer object for an ephemeral gem (one where we don't # actually have a .gem file, just a spec) def self.for_spec(spec, options = {}) # FIXME: we should have a real Package class for this new FakePackage.new(spec), options end ## # Constructs an Installer instance that will install the gem at +package+ which # can either be a path or an instance of Gem::Package. +options+ is a Hash # with the following keys: # # :bin_dir:: Where to put a bin wrapper if needed. # :development:: Whether or not development dependencies should be installed. # :env_shebang:: Use /usr/bin/env in bin wrappers. # :force:: Overrides all version checks and security policy checks, except # for a signed-gems-only policy. # :format_executable:: Format the executable the same as the Ruby executable. # If your Ruby is ruby18, foo_exec will be installed as # foo_exec18. # :ignore_dependencies:: Don't raise if a dependency is missing. # :install_dir:: The directory to install the gem into. # :security_policy:: Use the specified security policy. See Gem::Security # :user_install:: Indicate that the gem should be unpacked into the users # personal gem directory. # :only_install_dir:: Only validate dependencies against what is in the # install_dir # :wrappers:: Install wrappers if true, symlinks if false. # :build_args:: An Array of arguments to pass to the extension builder # process. If not set, then Gem::Command.build_args is used # :post_install_message:: Print gem post install message if true def initialize(package, options={}) require 'fileutils' @options = options @package = package process_options @package.dir_mode = options[:dir_mode] @package.prog_mode = options[:prog_mode] @package.data_mode = options[:data_mode] if options[:user_install] @gem_home = Gem.user_dir @bin_dir = Gem.bindir gem_home unless options[:bin_dir] @plugins_dir = Gem.plugindir(gem_home) check_that_user_bin_dir_is_in_path end end ## # Checks if +filename+ exists in +@bin_dir+. # # If +@force+ is set +filename+ is overwritten. # # If +filename+ exists and is a RubyGems wrapper for different gem the user # is consulted. # # If +filename+ exists and +@bin_dir+ is Gem.default_bindir (/usr/local) the # user is consulted. # # Otherwise +filename+ is overwritten. def check_executable_overwrite(filename) # :nodoc: return if @force generated_bin = File.join @bin_dir, formatted_program_filename(filename) return unless File.exist? generated_bin ruby_executable = false existing = nil File.open generated_bin, 'rb' do |io| next unless io.gets =~ /^#!/ # shebang io.gets # blankline # TODO detect a specially formatted comment instead of trying # to run a regexp against Ruby code. next unless io.gets =~ /This file was generated by RubyGems/ ruby_executable = true existing = io.read.slice(%r{ ^\s*( gem \s | load \s Gem\.bin_path\( | load \s Gem\.activate_bin_path\( ) (['"])(.*?)(\2), }x, 3) end return if spec.name == existing # somebody has written to RubyGems' directory, overwrite, too bad return if Gem.default_bindir != @bin_dir and not ruby_executable question = "#{spec.name}'s executable \"#{filename}\" conflicts with ".dup if ruby_executable question << (existing || 'an unknown executable') return if ask_yes_no "#{question}\nOverwrite the executable?", false conflict = "installed executable from #{existing}" else question << generated_bin return if ask_yes_no "#{question}\nOverwrite the executable?", false conflict = generated_bin end raise Gem::InstallError, "\"#{filename}\" from #{spec.name} conflicts with #{conflict}" end ## # Lazy accessor for the spec's gem directory. def gem_dir @gem_dir ||= File.join(gem_home, "gems", spec.full_name) end ## # Lazy accessor for the installer's spec. def spec @package.spec rescue Gem::Package::Error => e raise Gem::InstallError, "invalid gem: #{e.message}" end ## # Installs the gem and returns a loaded Gem::Specification for the installed # gem. # # The gem will be installed with the following structure: # # @gem_home/ # cache/<gem-version>.gem #=> a cached copy of the installed gem # gems/<gem-version>/... #=> extracted files # specifications/<gem-version>.gemspec #=> the Gem::Specification def install pre_install_checks run_pre_install_hooks # Set loaded_from to ensure extension_dir is correct if @options[:install_as_default] spec.loaded_from = default_spec_file else spec.loaded_from = spec_file end # Completely remove any previous gem files FileUtils.rm_rf gem_dir FileUtils.rm_rf spec.extension_dir dir_mode = options[:dir_mode] FileUtils.mkdir_p gem_dir, :mode => dir_mode && 0755 if @options[:install_as_default] extract_bin write_default_spec else extract_files build_extensions write_build_info_file run_post_build_hooks end generate_bin generate_plugins unless @options[:install_as_default] write_spec write_cache_file end File.chmod(dir_mode, gem_dir) if dir_mode say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil? Gem::Specification.reset run_post_install_hooks spec # TODO This rescue is in the wrong place. What is raising this exception? # move this rescue to around the code that actually might raise it. rescue Zlib::GzipFile::Error raise Gem::InstallError, "gzip error installing #{gem}" end def run_pre_install_hooks # :nodoc: Gem.pre_install_hooks.each do |hook| if hook.call(self) == false location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/ message = "pre-install hook#{location} failed for #{spec.full_name}" raise Gem::InstallError, message end end end def run_post_build_hooks # :nodoc: Gem.post_build_hooks.each do |hook| if hook.call(self) == false FileUtils.rm_rf gem_dir location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/ message = "post-build hook#{location} failed for #{spec.full_name}" raise Gem::InstallError, message end end end def run_post_install_hooks # :nodoc: Gem.post_install_hooks.each do |hook| hook.call self end end ## # # Return an Array of Specifications contained within the gem_home # we'll be installing into. def installed_specs @specs ||= begin specs = [] Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path| spec = Gem::Specification.load path.tap(&Gem::UNTAINT) specs << spec if spec end specs end end ## # Ensure that the dependency is satisfied by the current installation of # gem. If it is not an exception is raised. # # spec :: Gem::Specification # dependency :: Gem::Dependency def ensure_dependency(spec, dependency) unless installation_satisfies_dependency? dependency raise Gem::InstallError, "#{spec.name} requires #{dependency}" end true end ## # True if the gems in the system satisfy +dependency+. def installation_satisfies_dependency?(dependency) return true if @options[:development] and dependency.type == :development return true if installed_specs.detect {|s| dependency.matches_spec? s } return false if @only_install_dir not dependency.matching_specs.empty? end ## # Unpacks the gem into the given directory. def unpack(directory) @gem_dir = directory extract_files end rubygems_deprecate :unpack ## # The location of the spec file that is installed. # def spec_file File.join gem_home, "specifications", "#{spec.full_name}.gemspec" end ## # The location of the default spec file for default gems. # def default_spec_file File.join gem_home, "specifications", "default", "#{spec.full_name}.gemspec" end ## # Writes the .gemspec specification (in Ruby) to the gem home's # specifications directory. def write_spec spec.installed_by_version = Gem.rubygems_version Gem.write_binary(spec_file, spec.to_ruby_for_cache) end ## # Writes the full .gemspec specification (in Ruby) to the gem home's # specifications/default directory. def write_default_spec Gem.write_binary(default_spec_file, spec.to_ruby) end ## # Creates windows .bat files for easy running of commands def generate_windows_script(filename, bindir) if Gem.win_platform? script_name = formatted_program_filename(filename) + ".bat" script_path = File.join bindir, File.basename(script_name) File.open script_path, 'w' do |file| file.puts windows_stub_script(bindir, filename) end verbose script_path end end def generate_bin # :nodoc: return if spec.executables.nil? or spec.executables.empty? ensure_writable_dir @bin_dir spec.executables.each do |filename| filename.tap(&Gem::UNTAINT) bin_path = File.join gem_dir, spec.bindir, filename unless File.exist? bin_path if File.symlink? bin_path alert_warning "`#{bin_path}` is dangling symlink pointing to `#{File.readlink bin_path}`" else alert_warning "`#{bin_path}` does not exist, maybe `gem pristine #{spec.name}` will fix it?" end next end mode = File.stat(bin_path).mode dir_mode = options[:prog_mode] || (mode | 0111) unless dir_mode == mode require 'fileutils' FileUtils.chmod dir_mode, bin_path end check_executable_overwrite filename if @wrappers generate_bin_script filename, @bin_dir else generate_bin_symlink filename, @bin_dir end end end def generate_plugins # :nodoc: latest = Gem::Specification.latest_spec_for(spec.name) return if latest && latest.version > spec.version ensure_writable_dir @plugins_dir if spec.plugins.empty? remove_plugins_for(spec, @plugins_dir) else regenerate_plugins_for(spec, @plugins_dir) end end ## # Creates the scripts to run the applications in the gem. #-- # The Windows script is generated in addition to the regular one due to a # bug or misfeature in the Windows shell's pipe. See # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379 def generate_bin_script(filename, bindir) bin_script_path = File.join bindir, formatted_program_filename(filename) require 'fileutils' FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers File.open bin_script_path, 'wb', 0755 do |file| file.print app_script_text(filename) file.chmod(options[:prog_mode] || 0755) end verbose bin_script_path generate_windows_script filename, bindir end ## # Creates the symlinks to run the applications in the gem. Moves # the symlink if the gem being installed has a newer version. def generate_bin_symlink(filename, bindir) src = File.join gem_dir, spec.bindir, filename dst = File.join bindir, formatted_program_filename(filename) if File.exist? dst if File.symlink? dst link = File.readlink(dst).split File::SEPARATOR cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) return if spec.version < cur_version end File.unlink dst end FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose rescue NotImplementedError, SystemCallError alert_warning "Unable to use symlinks, installing wrapper" generate_bin_script filename, bindir end ## # Generates a #! line for +bin_file_name+'s wrapper copying arguments if # necessary. # # If the :custom_shebang config is set, then it is used as a template # for how to create the shebang used for to run a gem's executables. # # The template supports 4 expansions: # # $env the path to the unix env utility # $ruby the path to the currently running ruby interpreter # $exec the path to the gem's executable # $name the name of the gem the executable is for # def shebang(bin_file_name) ruby_name = RbConfig::CONFIG['ruby_install_name'] if @env_shebang path = File.join gem_dir, spec.bindir, bin_file_name first_line = File.open(path, "rb") {|file| file.gets } || "" if first_line.start_with?("#!") # Preserve extra words on shebang line, like "-w". Thanks RPA. shebang = first_line.sub(/\A\#!.*?ruby\S*((\s+\S+)+)/, "#!#{Gem.ruby}") opts = $1 shebang.strip! # Avoid nasty ^M issues. end if which = Gem.configuration[:custom_shebang] # replace bin_file_name with "ruby" to avoid endless loops which = which.gsub(/ #{bin_file_name}$/," #{RbConfig::CONFIG['ruby_install_name']}") which = which.gsub(/\$(\w+)/) do case $1 when "env" @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } when "ruby" "#{Gem.ruby}#{opts}" when "exec" bin_file_name when "name" spec.name end end "#!#{which}" elsif not ruby_name "#!#{Gem.ruby}#{opts}" elsif opts "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" else # Create a plain shebang line. @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } "#!#{@env_path} #{ruby_name}" end end ## # Ensures the Gem::Specification written out for this gem is loadable upon # installation. def ensure_loadable_spec ruby = spec.to_ruby_for_cache ruby.tap(&Gem::UNTAINT) begin eval ruby rescue StandardError, SyntaxError => e raise Gem::InstallError, "The specification for #{spec.full_name} is corrupt (#{e.class})" end end def ensure_dependencies_met # :nodoc: deps = spec.runtime_dependencies deps |= spec.development_dependencies if @development deps.each do |dep_gem| ensure_dependency spec, dep_gem end end def process_options # :nodoc: @options = { :bin_dir => nil, :env_shebang => false, :force => false, :only_install_dir => false, :post_install_message => true, }.merge options @env_shebang = options[:env_shebang] @force = options[:force] @install_dir = options[:install_dir] @gem_home = options[:install_dir] || Gem.dir @plugins_dir = Gem.plugindir(@gem_home) @ignore_dependencies = options[:ignore_dependencies] @format_executable = options[:format_executable] @wrappers = options[:wrappers] @only_install_dir = options[:only_install_dir] # If the user has asked for the gem to be installed in a directory that is # the system gem directory, then use the system bin directory, else create # (or use) a new bin dir under the gem_home. @bin_dir = options[:bin_dir] || Gem.bindir(gem_home) @development = options[:development] @build_root = options[:build_root] @build_args = options[:build_args] unless @build_root.nil? @bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, '')) @gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, '')) @plugins_dir = File.join(@build_root, @plugins_dir.gsub(/^[a-zA-Z]:/, '')) alert_warning "You build with buildroot.\n Build root: #{@build_root}\n Bin dir: #{@bin_dir}\n Gem home: #{@gem_home}\n Plugins dir: #{@plugins_dir}" end end def check_that_user_bin_dir_is_in_path # :nodoc: return if self.class.path_warning user_bin_dir = @bin_dir || Gem.bindir(gem_home) user_bin_dir = user_bin_dir.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR path = ENV['PATH'] path = path.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR if Gem.win_platform? path = path.downcase user_bin_dir = user_bin_dir.downcase end path = path.split(File::PATH_SEPARATOR) unless path.include? user_bin_dir unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV['HOME'], '~')) alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." self.class.path_warning = true end end end def verify_gem_home # :nodoc: FileUtils.mkdir_p gem_home, :mode => options[:dir_mode] && 0755 raise Gem::FilePermissionError, gem_home unless File.writable?(gem_home) end def verify_spec unless spec.name =~ Gem::Specification::VALID_NAME_PATTERN raise Gem::InstallError, "#{spec} has an invalid name" end if spec.raw_require_paths.any?{|path| path =~ /\R/ } raise Gem::InstallError, "#{spec} has an invalid require_paths" end if spec.extensions.any?{|ext| ext =~ /\R/ } raise Gem::InstallError, "#{spec} has an invalid extensions" end if spec.platform.to_s =~ /\R/ raise Gem::InstallError, "#{spec.platform} is an invalid platform" end unless spec.specification_version.to_s =~ /\A\d+\z/ raise Gem::InstallError, "#{spec} has an invalid specification_version" end if spec.dependencies.any? {|dep| dep.type != :runtime && dep.type != :development } raise Gem::InstallError, "#{spec} has an invalid dependencies" end if spec.dependencies.any? {|dep| dep.name =~ /(?:\R|[<>])/ } raise Gem::InstallError, "#{spec} has an invalid dependencies" end end ## # Return the text for an application file. def app_script_text(bin_file_name) # note that the `load` lines cannot be indented, as old RG versions match # against the beginning of the line return <<-TEXT #{shebang bin_file_name} # # This file was generated by RubyGems. # # The application '#{spec.name}' is installed as part of a gem, and # this file is here to facilitate running it. # require 'rubygems' #{gemdeps_load(spec.name)} version = "#{Gem::Requirement.default_prerelease}" str = ARGV.first if str str = str.b[/\\A_(.*)_\\z/, 1] if str and Gem::Version.correct?(str) version = str ARGV.shift end end if Gem.respond_to?(:activate_bin_path) load Gem.activate_bin_path('#{spec.name}', '#{bin_file_name}', version) else gem #{spec.name.dump}, version load Gem.bin_path(#{spec.name.dump}, #{bin_file_name.dump}, version) end TEXT end def gemdeps_load(name) return '' if name == "bundler" <<-TEXT Gem.use_gemdeps TEXT end ## # return the stub script text used to launch the true Ruby script def windows_stub_script(bindir, bin_file_name) rb_config = RbConfig::CONFIG rb_topdir = RbConfig::TOPDIR || File.dirname(rb_config["bindir"]) # get ruby executable file name from RbConfig ruby_exe = "#{rb_config['RUBY_INSTALL_NAME']}#{rb_config['EXEEXT']}" ruby_exe = "ruby.exe" if ruby_exe.empty? if File.exist?(File.join bindir, ruby_exe) # stub & ruby.exe within same folder. Portable <<-TEXT @ECHO OFF @"%~dp0#{ruby_exe}" "%~dpn0" %* TEXT elsif bindir.downcase.start_with? rb_topdir.downcase # stub within ruby folder, but not standard bin. Portable require 'pathname' from = Pathname.new bindir to = Pathname.new "#{rb_topdir}/bin" rel = to.relative_path_from from <<-TEXT @ECHO OFF @"%~dp0#{rel}/#{ruby_exe}" "%~dpn0" %* TEXT else # outside ruby folder, maybe -user-install or bundler. Portable, but ruby # is dependent on PATH <<-TEXT @ECHO OFF @#{ruby_exe} "%~dpn0" %* TEXT end end ## # Builds extensions. Valid types of extensions are extconf.rb files, # configure scripts and rakefiles or mkrf_conf files. def build_extensions builder = Gem::Ext::Builder.new spec, build_args builder.build_extensions end ## # Reads the file index and extracts each file into the gem directory. # # Ensures that files can't be installed outside the gem directory. def extract_files @package.extract_files gem_dir end ## # Extracts only the bin/ files from the gem into the gem directory. # This is used by default gems to allow a gem-aware stub to function # without the full gem installed. def extract_bin @package.extract_files gem_dir, "#{spec.bindir}/*" end ## # Prefix and suffix the program filename the same as ruby. def formatted_program_filename(filename) if @format_executable self.class.exec_format % File.basename(filename) else filename end end ## # # Return the target directory where the gem is to be installed. This # directory is not guaranteed to be populated. # def dir gem_dir.to_s end ## # Filename of the gem being installed. def gem @package.gem.path end ## # Performs various checks before installing the gem such as the install # repository is writable and its directories exist, required Ruby and # rubygems versions are met and that dependencies are installed. # # Version and dependency checks are skipped if this install is forced. # # The dependent check will be skipped if the install is ignoring dependencies. def pre_install_checks verify_gem_home # The name and require_paths must be verified first, since it could contain # ruby code that would be eval'ed in #ensure_loadable_spec verify_spec ensure_loadable_spec if options[:install_as_default] Gem.ensure_default_gem_subdirectories gem_home else Gem.ensure_gem_subdirectories gem_home end return true if @force ensure_dependencies_met unless @ignore_dependencies true end ## # Writes the file containing the arguments for building this gem's # extensions. def write_build_info_file return if build_args.empty? build_info_dir = File.join gem_home, 'build_info' dir_mode = options[:dir_mode] FileUtils.mkdir_p build_info_dir, :mode => dir_mode && 0755 build_info_file = File.join build_info_dir, "#{spec.full_name}.info" File.open build_info_file, 'w' do |io| build_args.each do |arg| io.puts arg end end File.chmod(dir_mode, build_info_dir) if dir_mode end ## # Writes the .gem file to the cache directory def write_cache_file cache_file = File.join gem_home, 'cache', spec.file_name @package.copy_to cache_file end def ensure_writable_dir(dir) # :nodoc: begin Dir.mkdir dir, *[options[:dir_mode] && 0755].compact rescue SystemCallError raise unless File.directory? dir end raise Gem::FilePermissionError.new(dir) unless File.writable? dir end private def build_args @build_args ||= begin require_relative "command" Gem::Command.build_args end end end rubygems/rubygems/name_tuple.rb 0000644 00000004633 15102661023 0012716 0 ustar 00 # frozen_string_literal: true ## # # Represents a gem of name +name+ at +version+ of +platform+. These # wrap the data returned from the indexes. class Gem::NameTuple def initialize(name, version, platform="ruby") @name = name @version = version unless platform.kind_of? Gem::Platform platform = "ruby" if !platform or platform.empty? end @platform = platform end attr_reader :name, :version, :platform ## # Turn an array of [name, version, platform] into an array of # NameTuple objects. def self.from_list(list) list.map {|t| new(*t) } end ## # Turn an array of NameTuple objects back into an array of # [name, version, platform] tuples. def self.to_basic(list) list.map {|t| t.to_a } end ## # A null NameTuple, ie name=nil, version=0 def self.null new nil, Gem::Version.new(0), nil end ## # Returns the full name (name-version) of this Gem. Platform information is # included if it is not the default Ruby platform. This mimics the behavior # of Gem::Specification#full_name. def full_name case @platform when nil, 'ruby', '' "#{@name}-#{@version}" else "#{@name}-#{@version}-#{@platform}" end.dup.tap(&Gem::UNTAINT) end ## # Indicate if this NameTuple matches the current platform. def match_platform? Gem::Platform.match_gem? @platform, @name end ## # Indicate if this NameTuple is for a prerelease version. def prerelease? @version.prerelease? end ## # Return the name that the gemspec file would be def spec_name "#{full_name}.gemspec" end ## # Convert back to the [name, version, platform] tuple def to_a [@name, @version, @platform] end def inspect # :nodoc: "#<Gem::NameTuple #{@name}, #{@version}, #{@platform}>" end alias to_s inspect # :nodoc: def <=>(other) [@name, @version, @platform == Gem::Platform::RUBY ? -1 : 1] <=> [other.name, other.version, other.platform == Gem::Platform::RUBY ? -1 : 1] end include Comparable ## # Compare with +other+. Supports another NameTuple or an Array # in the [name, version, platform] format. def ==(other) case other when self.class @name == other.name and @version == other.version and @platform == other.platform when Array to_a == other else false end end alias_method :eql?, :== def hash to_a.hash end end rubygems/rubygems/uri.rb 0000644 00000004174 15102661023 0011364 0 ustar 00 # frozen_string_literal: true ## # The Uri handles rubygems source URIs. # class Gem::Uri def initialize(source_uri) @parsed_uri = parse(source_uri) end def redacted return self unless valid_uri? if token? || oauth_basic? with_redacted_user elsif password? with_redacted_password else self end end def to_s @parsed_uri.to_s end def redact_credentials_from(text) return text unless valid_uri? && password? text.sub(password, 'REDACTED') end def method_missing(method_name, *args, &blk) if @parsed_uri.respond_to?(method_name) @parsed_uri.send(method_name, *args, &blk) else super end end def respond_to_missing?(method_name, include_private = false) @parsed_uri.respond_to?(method_name, include_private) || super end protected # Add a protected reader for the cloned instance to access the original object's parsed uri attr_reader :parsed_uri private ## # Parses the #uri, raising if it's invalid def parse!(uri) require "uri" raise URI::InvalidURIError unless uri # Always escape URI's to deal with potential spaces and such # It should also be considered that source_uri may already be # a valid URI with escaped characters. e.g. "{DESede}" is encoded # as "%7BDESede%7D". If this is escaped again the percentage # symbols will be escaped. begin URI.parse(uri) rescue URI::InvalidURIError URI.parse(URI::DEFAULT_PARSER.escape(uri)) end end ## # Parses the #uri, returning the original uri if it's invalid def parse(uri) return uri unless uri.is_a?(String) parse!(uri) rescue URI::InvalidURIError uri end def with_redacted_user clone.tap {|uri| uri.user = 'REDACTED' } end def with_redacted_password clone.tap {|uri| uri.password = 'REDACTED' } end def valid_uri? !@parsed_uri.is_a?(String) end def password? !!password end def oauth_basic? password == 'x-oauth-basic' end def token? !user.nil? && password.nil? end def initialize_copy(original) @parsed_uri = original.parsed_uri.clone end end rubygems/rubygems/deprecate.rb 0000644 00000006707 15102661023 0012525 0 ustar 00 # frozen_string_literal: true ## # Provides a single method +deprecate+ to be used to declare when # something is going away. # # class Legacy # def self.klass_method # # ... # end # # def instance_method # # ... # end # # extend Gem::Deprecate # deprecate :instance_method, "X.z", 2011, 4 # # class << self # extend Gem::Deprecate # deprecate :klass_method, :none, 2011, 4 # end # end module Gem::Deprecate def self.skip # :nodoc: @skip ||= false end def self.skip=(v) # :nodoc: @skip = v end ## # Temporarily turn off warnings. Intended for tests only. def skip_during Gem::Deprecate.skip, original = true, Gem::Deprecate.skip yield ensure Gem::Deprecate.skip = original end def self.next_rubygems_major_version # :nodoc: Gem::Version.new(Gem.rubygems_version.segments.first).bump end ## # Simple deprecation method that deprecates +name+ by wrapping it up # in a dummy method. It warns on each call to the dummy method # telling the user of +repl+ (unless +repl+ is :none) and the # year/month that it is planned to go away. def deprecate(name, repl, year, month) class_eval do old = "_deprecated_#{name}" alias_method old, name define_method name do |*args, &block| klass = self.kind_of? Module target = klass ? "#{self}." : "#{self.class}#" msg = [ "NOTE: #{target}#{name} is deprecated", repl == :none ? " with no replacement" : "; use #{repl} instead", ". It will be removed on or after %4d-%02d." % [year, month], "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}", ] warn "#{msg.join}." unless Gem::Deprecate.skip send old, *args, &block end ruby2_keywords name if respond_to?(:ruby2_keywords, true) end end ## # Simple deprecation method that deprecates +name+ by wrapping it up # in a dummy method. It warns on each call to the dummy method # telling the user of +repl+ (unless +repl+ is :none) and the # Rubygems version that it is planned to go away. def rubygems_deprecate(name, replacement=:none) class_eval do old = "_deprecated_#{name}" alias_method old, name define_method name do |*args, &block| klass = self.kind_of? Module target = klass ? "#{self}." : "#{self.class}#" msg = [ "NOTE: #{target}#{name} is deprecated", replacement == :none ? " with no replacement" : "; use #{replacement} instead", ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}", "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}", ] warn "#{msg.join}." unless Gem::Deprecate.skip send old, *args, &block end ruby2_keywords name if respond_to?(:ruby2_keywords, true) end end # Deprecation method to deprecate Rubygems commands def rubygems_deprecate_command class_eval do define_method "deprecated?" do true end define_method "deprecation_warning" do msg = [ "#{self.command} command is deprecated", ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}.\n", ] alert_warning "#{msg.join}" unless Gem::Deprecate.skip end end end module_function :rubygems_deprecate, :rubygems_deprecate_command, :skip_during end rubygems/rubygems/request_set.rb 0000644 00000026402 15102661023 0013126 0 ustar 00 # frozen_string_literal: true require_relative 'tsort' ## # A RequestSet groups a request to activate a set of dependencies. # # nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6' # pg = Gem::Dependency.new 'pg', '~> 0.14' # # set = Gem::RequestSet.new nokogiri, pg # # requests = set.resolve # # p requests.map { |r| r.full_name } # #=> ["nokogiri-1.6.0", "mini_portile-0.5.1", "pg-0.17.0"] class Gem::RequestSet include Gem::TSort ## # Array of gems to install even if already installed attr_accessor :always_install attr_reader :dependencies attr_accessor :development ## # Errors fetching gems during resolution. attr_reader :errors ## # Set to true if you want to install only direct development dependencies. attr_accessor :development_shallow ## # The set of git gems imported via load_gemdeps. attr_reader :git_set # :nodoc: ## # When true, dependency resolution is not performed, only the requested gems # are installed. attr_accessor :ignore_dependencies attr_reader :install_dir # :nodoc: ## # If true, allow dependencies to match prerelease gems. attr_accessor :prerelease ## # When false no remote sets are used for resolving gems. attr_accessor :remote attr_reader :resolver # :nodoc: ## # Sets used for resolution attr_reader :sets # :nodoc: ## # Treat missing dependencies as silent errors attr_accessor :soft_missing ## # The set of vendor gems imported via load_gemdeps. attr_reader :vendor_set # :nodoc: ## # The set of source gems imported via load_gemdeps. attr_reader :source_set ## # Creates a RequestSet for a list of Gem::Dependency objects, +deps+. You # can then #resolve and #install the resolved list of dependencies. # # nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6' # pg = Gem::Dependency.new 'pg', '~> 0.14' # # set = Gem::RequestSet.new nokogiri, pg def initialize(*deps) @dependencies = deps @always_install = [] @conservative = false @dependency_names = {} @development = false @development_shallow = false @errors = [] @git_set = nil @ignore_dependencies = false @install_dir = Gem.dir @prerelease = false @remote = true @requests = [] @sets = [] @soft_missing = false @sorted = nil @specs = nil @vendor_set = nil @source_set = nil yield self if block_given? end ## # Declare that a gem of name +name+ with +reqs+ requirements is needed. def gem(name, *reqs) if dep = @dependency_names[name] dep.requirement.concat reqs else dep = Gem::Dependency.new name, *reqs @dependency_names[name] = dep @dependencies << dep end end ## # Add +deps+ Gem::Dependency objects to the set. def import(deps) @dependencies.concat deps end ## # Installs gems for this RequestSet using the Gem::Installer +options+. # # If a +block+ is given an activation +request+ and +installer+ are yielded. # The +installer+ will be +nil+ if a gem matching the request was already # installed. def install(options, &block) # :yields: request, installer if dir = options[:install_dir] requests = install_into dir, false, options, &block return requests end @prerelease = options[:prerelease] requests = [] download_queue = Thread::Queue.new # Create a thread-safe list of gems to download sorted_requests.each do |req| download_queue << req end # Create N threads in a pool, have them download all the gems threads = Gem.configuration.concurrent_downloads.times.map do # When a thread pops this item, it knows to stop running. The symbol # is queued here so that there will be one symbol per thread. download_queue << :stop Thread.new do # The pop method will block waiting for items, so the only way # to stop a thread from running is to provide a final item that # means the thread should stop. while req = download_queue.pop break if req == :stop req.spec.download options unless req.installed? end end end # Wait for all the downloads to finish before continuing threads.each(&:value) # Install requested gems after they have been downloaded sorted_requests.each do |req| if req.installed? req.spec.spec.build_extensions if @always_install.none? {|spec| spec == req.spec.spec } yield req, nil if block_given? next end end spec = begin req.spec.install options do |installer| yield req, installer if block_given? end rescue Gem::RuntimeRequirementNotMetError => e suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems" suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec) e.suggestion = suggestion raise end requests << spec end return requests if options[:gemdeps] install_hooks requests, options requests end ## # Installs from the gem dependencies files in the +:gemdeps+ option in # +options+, yielding to the +block+ as in #install. # # If +:without_groups+ is given in the +options+, those groups in the gem # dependencies file are not used. See Gem::Installer for other +options+. def install_from_gemdeps(options, &block) gemdeps = options[:gemdeps] @install_dir = options[:install_dir] || Gem.dir @prerelease = options[:prerelease] @remote = options[:domain] != :local @conservative = true if options[:conservative] gem_deps_api = load_gemdeps gemdeps, options[:without_groups], true resolve if options[:explain] puts "Gems to install:" sorted_requests.each do |spec| puts " #{spec.full_name}" end if Gem.configuration.really_verbose @resolver.stats.display end else installed = install options, &block if options.fetch :lock, true lockfile = Gem::RequestSet::Lockfile.build self, gemdeps, gem_deps_api.dependencies lockfile.write end installed end end def install_into(dir, force = true, options = {}) gem_home, ENV['GEM_HOME'] = ENV['GEM_HOME'], dir existing = force ? [] : specs_in(dir) existing.delete_if {|s| @always_install.include? s } dir = File.expand_path dir installed = [] options[:development] = false options[:install_dir] = dir options[:only_install_dir] = true @prerelease = options[:prerelease] sorted_requests.each do |request| spec = request.spec if existing.find {|s| s.full_name == spec.full_name } yield request, nil if block_given? next end spec.install options do |installer| yield request, installer if block_given? end installed << request end install_hooks installed, options installed ensure ENV['GEM_HOME'] = gem_home end ## # Call hooks on installed gems def install_hooks(requests, options) specs = requests.map do |request| case request when Gem::Resolver::ActivationRequest then request.spec.spec else request end end require_relative "dependency_installer" inst = Gem::DependencyInstaller.new options inst.installed_gems.replace specs Gem.done_installing_hooks.each do |hook| hook.call inst, specs end unless Gem.done_installing_hooks.empty? end ## # Load a dependency management file. def load_gemdeps(path, without_groups = [], installing = false) @git_set = Gem::Resolver::GitSet.new @vendor_set = Gem::Resolver::VendorSet.new @source_set = Gem::Resolver::SourceSet.new @git_set.root_dir = @install_dir lock_file = "#{File.expand_path(path)}.lock".dup.tap(&Gem::UNTAINT) begin tokenizer = Gem::RequestSet::Lockfile::Tokenizer.from_file lock_file parser = tokenizer.make_parser self, [] parser.parse rescue Errno::ENOENT end gf = Gem::RequestSet::GemDependencyAPI.new self, path gf.installing = installing gf.without_groups = without_groups if without_groups gf.load end def pretty_print(q) # :nodoc: q.group 2, '[RequestSet:', ']' do q.breakable if @remote q.text 'remote' q.breakable end if @prerelease q.text 'prerelease' q.breakable end if @development_shallow q.text 'shallow development' q.breakable elsif @development q.text 'development' q.breakable end if @soft_missing q.text 'soft missing' end q.group 2, '[dependencies:', ']' do q.breakable @dependencies.map do |dep| q.text dep.to_s q.breakable end end q.breakable q.text 'sets:' q.breakable q.pp @sets.map {|set| set.class } end end ## # Resolve the requested dependencies and return an Array of Specification # objects to be activated. def resolve(set = Gem::Resolver::BestSet.new) @sets << set @sets << @git_set @sets << @vendor_set @sets << @source_set set = Gem::Resolver.compose_sets(*@sets) set.remote = @remote set.prerelease = @prerelease resolver = Gem::Resolver.new @dependencies, set resolver.development = @development resolver.development_shallow = @development_shallow resolver.ignore_dependencies = @ignore_dependencies resolver.soft_missing = @soft_missing if @conservative installed_gems = {} Gem::Specification.find_all do |spec| (installed_gems[spec.name] ||= []) << spec end resolver.skip_gems = installed_gems end @resolver = resolver @requests = resolver.resolve @errors = set.errors @requests end ## # Resolve the requested dependencies against the gems available via Gem.path # and return an Array of Specification objects to be activated. def resolve_current resolve Gem::Resolver::CurrentSet.new end def sorted_requests @sorted ||= strongly_connected_components.flatten end def specs @specs ||= @requests.map {|r| r.full_spec } end def specs_in(dir) Gem::Util.glob_files_in_dir("*.gemspec", File.join(dir, "specifications")).map do |g| Gem::Specification.load g end end def tsort_each_node(&block) # :nodoc: @requests.each(&block) end def tsort_each_child(node) # :nodoc: node.spec.dependencies.each do |dep| next if dep.type == :development and not @development match = @requests.find do |r| dep.match? r.spec.name, r.spec.version, @prerelease end unless match next if dep.type == :development and @development_shallow next if @soft_missing raise Gem::DependencyError, "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})" end yield match end end end require_relative 'request_set/gem_dependency_api' require_relative 'request_set/lockfile' require_relative 'request_set/lockfile/tokenizer' rubygems/rubygems/dependency.rb 0000644 00000021143 15102661023 0012676 0 ustar 00 # frozen_string_literal: true ## # The Dependency class holds a Gem name and a Gem::Requirement. class Gem::Dependency ## # Valid dependency types. #-- # When this list is updated, be sure to change # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well. # # REFACTOR: This type of constant, TYPES, indicates we might want # two classes, used via inheritance or duck typing. TYPES = [ :development, :runtime, ].freeze ## # Dependency name or regular expression. attr_accessor :name ## # Allows you to force this dependency to be a prerelease. attr_writer :prerelease ## # Constructs a dependency with +name+ and +requirements+. The last # argument can optionally be the dependency type, which defaults to # <tt>:runtime</tt>. def initialize(name, *requirements) case name when String then # ok when Regexp then msg = ["NOTE: Dependency.new w/ a regexp is deprecated.", "Dependency.new called from #{Gem.location_of_caller.join(":")}"] warn msg.join("\n") unless Gem::Deprecate.skip else raise ArgumentError, "dependency name must be a String, was #{name.inspect}" end type = Symbol === requirements.last ? requirements.pop : :runtime requirements = requirements.first if 1 == requirements.length # unpack unless TYPES.include? type raise ArgumentError, "Valid types are #{TYPES.inspect}, " + "not #{type.inspect}" end @name = name @requirement = Gem::Requirement.create requirements @type = type @prerelease = false # This is for Marshal backwards compatibility. See the comments in # +requirement+ for the dirty details. @version_requirements = @requirement end ## # A dependency's hash is the XOR of the hashes of +name+, +type+, # and +requirement+. def hash # :nodoc: name.hash ^ type.hash ^ requirement.hash end def inspect # :nodoc: if prerelease? "<%s type=%p name=%p requirements=%p prerelease=ok>" % [self.class, self.type, self.name, requirement.to_s] else "<%s type=%p name=%p requirements=%p>" % [self.class, self.type, self.name, requirement.to_s] end end ## # Does this dependency require a prerelease? def prerelease? @prerelease || requirement.prerelease? end ## # Is this dependency simply asking for the latest version # of a gem? def latest_version? @requirement.none? end def pretty_print(q) # :nodoc: q.group 1, 'Gem::Dependency.new(', ')' do q.pp name q.text ',' q.breakable q.pp requirement q.text ',' q.breakable q.pp type end end ## # What does this dependency require? def requirement return @requirement if defined?(@requirement) and @requirement # @version_requirements and @version_requirement are legacy ivar # names, and supported here because older gems need to keep # working and Dependency doesn't implement marshal_dump and # marshal_load. In a happier world, this would be an # attr_accessor. The horrifying instance_variable_get you see # below is also the legacy of some old restructurings. # # Note also that because of backwards compatibility (loading new # gems in an old RubyGems installation), we can't add explicit # marshaling to this class until we want to make a big # break. Maybe 2.0. # # Children, define explicit marshal and unmarshal behavior for # public classes. Marshal formats are part of your public API. # REFACTOR: See above if defined?(@version_requirement) && @version_requirement version = @version_requirement.instance_variable_get :@version @version_requirement = nil @version_requirements = Gem::Requirement.new version end @requirement = @version_requirements if defined?(@version_requirements) end def requirements_list requirement.as_list end def to_s # :nodoc: if type != :runtime "#{name} (#{requirement}, #{type})" else "#{name} (#{requirement})" end end ## # Dependency type. def type @type ||= :runtime end def runtime? @type == :runtime || !@type end def ==(other) # :nodoc: Gem::Dependency === other && self.name == other.name && self.type == other.type && self.requirement == other.requirement end ## # Dependencies are ordered by name. def <=>(other) self.name <=> other.name end ## # Uses this dependency as a pattern to compare to +other+. This # dependency will match if the name matches the other's name, and # other has only an equal version requirement that satisfies this # dependency. def =~(other) unless Gem::Dependency === other return unless other.respond_to?(:name) && other.respond_to?(:version) other = Gem::Dependency.new other.name, other.version end return false unless name === other.name reqs = other.requirement.requirements return false unless reqs.length == 1 return false unless reqs.first.first == '=' version = reqs.first.last requirement.satisfied_by? version end alias === =~ ## # :call-seq: # dep.match? name => true or false # dep.match? name, version => true or false # dep.match? spec => true or false # # Does this dependency match the specification described by +name+ and # +version+ or match +spec+? # # NOTE: Unlike #matches_spec? this method does not return true when the # version is a prerelease version unless this is a prerelease dependency. def match?(obj, version=nil, allow_prerelease=false) if !version name = obj.name version = obj.version else name = obj end return false unless self.name === name version = Gem::Version.new version return true if requirement.none? and not version.prerelease? return false if version.prerelease? and not allow_prerelease and not prerelease? requirement.satisfied_by? version end ## # Does this dependency match +spec+? # # NOTE: This is not a convenience method. Unlike #match? this method # returns true when +spec+ is a prerelease version even if this dependency # is not a prerelease dependency. def matches_spec?(spec) return false unless name === spec.name return true if requirement.none? requirement.satisfied_by?(spec.version) end ## # Merges the requirements of +other+ into this dependency def merge(other) unless name == other.name raise ArgumentError, "#{self} and #{other} have different names" end default = Gem::Requirement.default self_req = self.requirement other_req = other.requirement return self.class.new name, self_req if other_req == default return self.class.new name, other_req if self_req == default self.class.new name, self_req.as_list.concat(other_req.as_list) end def matching_specs(platform_only = false) env_req = Gem.env_requirement(name) matches = Gem::Specification.stubs_for(name).find_all do |spec| requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) end.map(&:to_spec) Gem::BundlerVersionFinder.filter!(matches) if filters_bundler? if platform_only matches.reject! do |spec| spec.nil? || !Gem::Platform.match_spec?(spec) end end matches end ## # True if the dependency will not always match the latest version. def specific? @requirement.specific? end def filters_bundler? name == "bundler".freeze && !specific? end def to_specs matches = matching_specs true # TODO: check Gem.activated_spec[self.name] in case matches falls outside if matches.empty? specs = Gem::Specification.stubs_for name if specs.empty? raise Gem::MissingSpecError.new name, requirement else raise Gem::MissingSpecVersionError.new name, requirement, specs end end # TODO: any other resolver validations should go here matches end def to_spec matches = self.to_specs.compact active = matches.find {|spec| spec.activated? } return active if active return matches.first if prerelease? # Move prereleases to the end of the list for >= 0 requirements pre, matches = matches.partition {|spec| spec.version.prerelease? } matches += pre if requirement == Gem::Requirement.default matches.first end def identity if prerelease? if specific? :complete else :abs_latest end elsif latest_version? :latest else :released end end end rubygems/rubygems/tsort.rb 0000644 00000000102 15102661023 0011723 0 ustar 00 # frozen_string_literal: true require_relative 'tsort/lib/tsort' rubygems/rubygems/ext.rb 0000644 00000000714 15102661023 0011361 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ ## # Classes for building C extensions live here. module Gem::Ext; end require_relative 'ext/build_error' require_relative 'ext/builder' require_relative 'ext/configure_builder' require_relative 'ext/ext_conf_builder' require_relative 'ext/rake_builder' require_relative 'ext/cmake_builder' rubygems/rubygems/security/trust_dir.rb 0000644 00000005037 15102661023 0014452 0 ustar 00 # frozen_string_literal: true ## # The TrustDir manages the trusted certificates for gem signature # verification. class Gem::Security::TrustDir ## # Default permissions for the trust directory and its contents DEFAULT_PERMISSIONS = { :trust_dir => 0700, :trusted_cert => 0600, }.freeze ## # The directory where trusted certificates will be stored. attr_reader :dir ## # Creates a new TrustDir using +dir+ where the directory and file # permissions will be checked according to +permissions+ def initialize(dir, permissions = DEFAULT_PERMISSIONS) @dir = dir @permissions = permissions @digester = Gem::Security.create_digest end ## # Returns the path to the trusted +certificate+ def cert_path(certificate) name_path certificate.subject end ## # Enumerates trusted certificates. def each_certificate return enum_for __method__ unless block_given? glob = File.join @dir, '*.pem' Dir[glob].each do |certificate_file| begin certificate = load_certificate certificate_file yield certificate, certificate_file rescue OpenSSL::X509::CertificateError next # HACK warn end end end ## # Returns the issuer certificate of the given +certificate+ if it exists in # the trust directory. def issuer_of(certificate) path = name_path certificate.issuer return unless File.exist? path load_certificate path end ## # Returns the path to the trusted certificate with the given ASN.1 +name+ def name_path(name) digest = @digester.hexdigest name.to_s File.join @dir, "cert-#{digest}.pem" end ## # Loads the given +certificate_file+ def load_certificate(certificate_file) pem = File.read certificate_file OpenSSL::X509::Certificate.new pem end ## # Add a certificate to trusted certificate list. def trust_cert(certificate) verify destination = cert_path certificate File.open destination, 'wb', 0600 do |io| io.write certificate.to_pem io.chmod(@permissions[:trusted_cert]) end end ## # Make sure the trust directory exists. If it does exist, make sure it's # actually a directory. If not, then create it with the appropriate # permissions. def verify require 'fileutils' if File.exist? @dir raise Gem::Security::Exception, "trust directory #{@dir} is not a directory" unless File.directory? @dir FileUtils.chmod 0700, @dir else FileUtils.mkdir_p @dir, :mode => @permissions[:trust_dir] end end end rubygems/rubygems/security/signer.rb 0000644 00000013416 15102661023 0013722 0 ustar 00 # frozen_string_literal: true ## # Basic OpenSSL-based package signing class. require_relative "../user_interaction" class Gem::Security::Signer include Gem::UserInteraction ## # The chain of certificates for signing including the signing certificate attr_accessor :cert_chain ## # The private key for the signing certificate attr_accessor :key ## # The digest algorithm used to create the signature attr_reader :digest_algorithm ## # The name of the digest algorithm, used to pull digests out of the hash by # name. attr_reader :digest_name # :nodoc: ## # Gem::Security::Signer options attr_reader :options DEFAULT_OPTIONS = { expiration_length_days: 365, }.freeze ## # Attempts to re-sign an expired cert with a given private key def self.re_sign_cert(expired_cert, expired_cert_path, private_key) return unless expired_cert.not_after < Time.now expiry = expired_cert.not_after.strftime('%Y%m%d%H%M%S') expired_cert_file = "#{File.basename(expired_cert_path)}.expired.#{expiry}" new_expired_cert_path = File.join(Gem.user_home, ".gem", expired_cert_file) Gem::Security.write(expired_cert, new_expired_cert_path) re_signed_cert = Gem::Security.re_sign( expired_cert, private_key, (Gem::Security::ONE_DAY * Gem.configuration.cert_expiration_length_days) ) Gem::Security.write(re_signed_cert, expired_cert_path) yield(expired_cert_path, new_expired_cert_path) if block_given? end ## # Creates a new signer with an RSA +key+ or path to a key, and a certificate # +chain+ containing X509 certificates, encoding certificates or paths to # certificates. def initialize(key, cert_chain, passphrase = nil, options = {}) @cert_chain = cert_chain @key = key @passphrase = passphrase @options = DEFAULT_OPTIONS.merge(options) unless @key default_key = File.join Gem.default_key_path @key = default_key if File.exist? default_key end unless @cert_chain default_cert = File.join Gem.default_cert_path @cert_chain = [default_cert] if File.exist? default_cert end @digest_name = Gem::Security::DIGEST_NAME @digest_algorithm = Gem::Security.create_digest(@digest_name) if @key && !@key.is_a?(OpenSSL::PKey::PKey) @key = OpenSSL::PKey.read(File.read(@key), @passphrase) end if @cert_chain @cert_chain = @cert_chain.compact.map do |cert| next cert if OpenSSL::X509::Certificate === cert cert = File.read cert if File.exist? cert OpenSSL::X509::Certificate.new cert end load_cert_chain end end ## # Extracts the full name of +cert+. If the certificate has a subjectAltName # this value is preferred, otherwise the subject is used. def extract_name(cert) # :nodoc: subject_alt_name = cert.extensions.find {|e| 'subjectAltName' == e.oid } if subject_alt_name /\Aemail:/ =~ subject_alt_name.value # rubocop:disable Performance/StartWith $' || subject_alt_name.value else cert.subject end end ## # Loads any missing issuers in the cert chain from the trusted certificates. # # If the issuer does not exist it is ignored as it will be checked later. def load_cert_chain # :nodoc: return if @cert_chain.empty? while @cert_chain.first.issuer.to_s != @cert_chain.first.subject.to_s do issuer = Gem::Security.trust_dir.issuer_of @cert_chain.first break unless issuer # cert chain is verified later @cert_chain.unshift issuer end end ## # Sign data with given digest algorithm def sign(data) return unless @key raise Gem::Security::Exception, 'no certs provided' if @cert_chain.empty? if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now alert("Your certificate has expired, trying to re-sign it...") re_sign_key( expiration_length: (Gem::Security::ONE_DAY * options[:expiration_length_days]) ) end full_name = extract_name @cert_chain.last Gem::Security::SigningPolicy.verify @cert_chain, @key, {}, {}, full_name @key.sign @digest_algorithm.new, data end ## # Attempts to re-sign the private key if the signing certificate is expired. # # The key will be re-signed if: # * The expired certificate is self-signed # * The expired certificate is saved at ~/.gem/gem-public_cert.pem # and the private key is saved at ~/.gem/gem-private_key.pem # * There is no file matching the expiry date at # ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S # # If the signing certificate can be re-signed the expired certificate will # be saved as ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S where the # expiry time (not after) is used for the timestamp. def re_sign_key(expiration_length: Gem::Security::ONE_YEAR) # :nodoc: old_cert = @cert_chain.last disk_cert_path = File.join(Gem.default_cert_path) disk_cert = File.read(disk_cert_path) rescue nil disk_key_path = File.join(Gem.default_key_path) disk_key = OpenSSL::PKey.read(File.read(disk_key_path), @passphrase) rescue nil return unless disk_key if disk_key.to_pem == @key.to_pem && disk_cert == old_cert.to_pem expiry = old_cert.not_after.strftime('%Y%m%d%H%M%S') old_cert_file = "gem-public_cert.pem.expired.#{expiry}" old_cert_path = File.join(Gem.user_home, ".gem", old_cert_file) unless File.exist?(old_cert_path) Gem::Security.write(old_cert, old_cert_path) cert = Gem::Security.re_sign(old_cert, @key, expiration_length) Gem::Security.write(cert, disk_cert_path) alert("Your cert: #{disk_cert_path} has been auto re-signed with the key: #{disk_key_path}") alert("Your expired cert will be located at: #{old_cert_path}") @cert_chain = [cert] end end end end rubygems/rubygems/security/policy.rb 0000644 00000017507 15102661023 0013737 0 ustar 00 # frozen_string_literal: true require_relative '../user_interaction' ## # A Gem::Security::Policy object encapsulates the settings for verifying # signed gem files. This is the base class. You can either declare an # instance of this or use one of the preset security policies in # Gem::Security::Policies. class Gem::Security::Policy include Gem::UserInteraction attr_reader :name attr_accessor :only_signed attr_accessor :only_trusted attr_accessor :verify_chain attr_accessor :verify_data attr_accessor :verify_root attr_accessor :verify_signer ## # Create a new Gem::Security::Policy object with the given mode and # options. def initialize(name, policy = {}, opt = {}) @name = name @opt = opt # Default to security @only_signed = true @only_trusted = true @verify_chain = true @verify_data = true @verify_root = true @verify_signer = true policy.each_pair do |key, val| case key when :verify_data then @verify_data = val when :verify_signer then @verify_signer = val when :verify_chain then @verify_chain = val when :verify_root then @verify_root = val when :only_trusted then @only_trusted = val when :only_signed then @only_signed = val end end end ## # Verifies each certificate in +chain+ has signed the following certificate # and is valid for the given +time+. def check_chain(chain, time) raise Gem::Security::Exception, 'missing signing chain' unless chain raise Gem::Security::Exception, 'empty signing chain' if chain.empty? begin chain.each_cons 2 do |issuer, cert| check_cert cert, issuer, time end true rescue Gem::Security::Exception => e raise Gem::Security::Exception, "invalid signing chain: #{e.message}" end end ## # Verifies that +data+ matches the +signature+ created by +public_key+ and # the +digest+ algorithm. def check_data(public_key, digest, signature, data) raise Gem::Security::Exception, "invalid signature" unless public_key.verify digest, signature, data.digest true end ## # Ensures that +signer+ is valid for +time+ and was signed by the +issuer+. # If the +issuer+ is +nil+ no verification is performed. def check_cert(signer, issuer, time) raise Gem::Security::Exception, 'missing signing certificate' unless signer message = "certificate #{signer.subject}" if not_before = signer.not_before and not_before > time raise Gem::Security::Exception, "#{message} not valid before #{not_before}" end if not_after = signer.not_after and not_after < time raise Gem::Security::Exception, "#{message} not valid after #{not_after}" end if issuer and not signer.verify issuer.public_key raise Gem::Security::Exception, "#{message} was not issued by #{issuer.subject}" end true end ## # Ensures the public key of +key+ matches the public key in +signer+ def check_key(signer, key) unless signer and key return true unless @only_signed raise Gem::Security::Exception, 'missing key or signature' end raise Gem::Security::Exception, "certificate #{signer.subject} does not match the signing key" unless signer.check_private_key(key) true end ## # Ensures the root certificate in +chain+ is self-signed and valid for # +time+. def check_root(chain, time) raise Gem::Security::Exception, 'missing signing chain' unless chain root = chain.first raise Gem::Security::Exception, 'missing root certificate' unless root raise Gem::Security::Exception, "root certificate #{root.subject} is not self-signed " + "(issuer #{root.issuer})" if root.issuer != root.subject check_cert root, root, time end ## # Ensures the root of +chain+ has a trusted certificate in +trust_dir+ and # the digests of the two certificates match according to +digester+ def check_trust(chain, digester, trust_dir) raise Gem::Security::Exception, 'missing signing chain' unless chain root = chain.first raise Gem::Security::Exception, 'missing root certificate' unless root path = Gem::Security.trust_dir.cert_path root unless File.exist? path message = "root cert #{root.subject} is not trusted".dup message << " (root of signing cert #{chain.last.subject})" if chain.length > 1 raise Gem::Security::Exception, message end save_cert = OpenSSL::X509::Certificate.new File.read path save_dgst = digester.digest save_cert.public_key.to_pem pkey_str = root.public_key.to_pem cert_dgst = digester.digest pkey_str raise Gem::Security::Exception, "trusted root certificate #{root.subject} checksum " + "does not match signing root certificate checksum" unless save_dgst == cert_dgst true end ## # Extracts the email or subject from +certificate+ def subject(certificate) # :nodoc: certificate.extensions.each do |extension| next unless extension.oid == 'subjectAltName' return extension.value end certificate.subject.to_s end def inspect # :nodoc: ("[Policy: %s - data: %p signer: %p chain: %p root: %p " + "signed-only: %p trusted-only: %p]") % [ @name, @verify_chain, @verify_data, @verify_root, @verify_signer, @only_signed, @only_trusted ] end ## # For +full_name+, verifies the certificate +chain+ is valid, the +digests+ # match the signatures +signatures+ created by the signer depending on the # +policy+ settings. # # If +key+ is given it is used to validate the signing certificate. def verify(chain, key = nil, digests = {}, signatures = {}, full_name = '(unknown)') if signatures.empty? if @only_signed raise Gem::Security::Exception, "unsigned gems are not allowed by the #{name} policy" elsif digests.empty? # lack of signatures is irrelevant if there is nothing to check # against else alert_warning "#{full_name} is not signed" return end end opt = @opt digester = Gem::Security.create_digest trust_dir = opt[:trust_dir] time = Time.now _, signer_digests = digests.find do |algorithm, file_digests| file_digests.values.first.name == Gem::Security::DIGEST_NAME end if @verify_data raise Gem::Security::Exception, 'no digests provided (probable bug)' if signer_digests.nil? or signer_digests.empty? else signer_digests = {} end signer = chain.last check_key signer, key if key check_cert signer, nil, time if @verify_signer check_chain chain, time if @verify_chain check_root chain, time if @verify_root if @only_trusted check_trust chain, digester, trust_dir elsif signatures.empty? and digests.empty? # trust is irrelevant if there's no signatures to verify else alert_warning "#{subject signer} is not trusted for #{full_name}" end signatures.each do |file, _| digest = signer_digests[file] raise Gem::Security::Exception, "missing digest for #{file}" unless digest end signer_digests.each do |file, digest| signature = signatures[file] raise Gem::Security::Exception, "missing signature for #{file}" unless signature check_data signer.public_key, digester, signature, digest if @verify_data end true end ## # Extracts the certificate chain from the +spec+ and calls #verify to ensure # the signatures and certificate chain is valid according to the policy.. def verify_signatures(spec, digests, signatures) chain = spec.cert_chain.map do |cert_pem| OpenSSL::X509::Certificate.new cert_pem end verify chain, nil, digests, signatures, spec.full_name true end alias to_s name # :nodoc: end rubygems/rubygems/security/policies.rb 0000644 00000006531 15102661023 0014242 0 ustar 00 # frozen_string_literal: true module Gem::Security ## # No security policy: all package signature checks are disabled. NoSecurity = Policy.new( 'No Security', :verify_data => false, :verify_signer => false, :verify_chain => false, :verify_root => false, :only_trusted => false, :only_signed => false ) ## # AlmostNo security policy: only verify that the signing certificate is the # one that actually signed the data. Make no attempt to verify the signing # certificate chain. # # This policy is basically useless. better than nothing, but can still be # easily spoofed, and is not recommended. AlmostNoSecurity = Policy.new( 'Almost No Security', :verify_data => true, :verify_signer => false, :verify_chain => false, :verify_root => false, :only_trusted => false, :only_signed => false ) ## # Low security policy: only verify that the signing certificate is actually # the gem signer, and that the signing certificate is valid. # # This policy is better than nothing, but can still be easily spoofed, and # is not recommended. LowSecurity = Policy.new( 'Low Security', :verify_data => true, :verify_signer => true, :verify_chain => false, :verify_root => false, :only_trusted => false, :only_signed => false ) ## # Medium security policy: verify the signing certificate, verify the signing # certificate chain all the way to the root certificate, and only trust root # certificates that we have explicitly allowed trust for. # # This security policy is reasonable, but it allows unsigned packages, so a # malicious person could simply delete the package signature and pass the # gem off as unsigned. MediumSecurity = Policy.new( 'Medium Security', :verify_data => true, :verify_signer => true, :verify_chain => true, :verify_root => true, :only_trusted => true, :only_signed => false ) ## # High security policy: only allow signed gems to be installed, verify the # signing certificate, verify the signing certificate chain all the way to # the root certificate, and only trust root certificates that we have # explicitly allowed trust for. # # This security policy is significantly more difficult to bypass, and offers # a reasonable guarantee that the contents of the gem have not been altered. HighSecurity = Policy.new( 'High Security', :verify_data => true, :verify_signer => true, :verify_chain => true, :verify_root => true, :only_trusted => true, :only_signed => true ) ## # Policy used to verify a certificate and key when signing a gem SigningPolicy = Policy.new( 'Signing Policy', :verify_data => false, :verify_signer => true, :verify_chain => true, :verify_root => true, :only_trusted => false, :only_signed => false ) ## # Hash of configured security policies Policies = { 'NoSecurity' => NoSecurity, 'AlmostNoSecurity' => AlmostNoSecurity, 'LowSecurity' => LowSecurity, 'MediumSecurity' => MediumSecurity, 'HighSecurity' => HighSecurity, # SigningPolicy is not intended for use by `gem -P` so do not list it }.freeze end rubygems/rubygems/doctor.rb 0000644 00000006266 15102661023 0012063 0 ustar 00 # frozen_string_literal: true require_relative '../rubygems' require_relative 'user_interaction' ## # Cleans up after a partially-failed uninstall or for an invalid # Gem::Specification. # # If a specification was removed by hand this will remove any remaining files. # # If a corrupt specification was installed this will clean up warnings by # removing the bogus specification. class Gem::Doctor include Gem::UserInteraction ## # Maps a gem subdirectory to the files that are expected to exist in the # subdirectory. REPOSITORY_EXTENSION_MAP = [ # :nodoc: ['specifications', '.gemspec'], ['build_info', '.info'], ['cache', '.gem'], ['doc', ''], ['extensions', ''], ['gems', ''], ['plugins', ''], ].freeze missing = Gem::REPOSITORY_SUBDIRECTORIES.sort - REPOSITORY_EXTENSION_MAP.map {|(k,_)| k }.sort raise "Update REPOSITORY_EXTENSION_MAP, missing: #{missing.join ', '}" unless missing.empty? ## # Creates a new Gem::Doctor that will clean up +gem_repository+. Only one # gem repository may be cleaned at a time. # # If +dry_run+ is true no files or directories will be removed. def initialize(gem_repository, dry_run = false) @gem_repository = gem_repository @dry_run = dry_run @installed_specs = nil end ## # Specs installed in this gem repository def installed_specs # :nodoc: @installed_specs ||= Gem::Specification.map {|s| s.full_name } end ## # Are we doctoring a gem repository? def gem_repository? not installed_specs.empty? end ## # Cleans up uninstalled files and invalid gem specifications def doctor @orig_home = Gem.dir @orig_path = Gem.path say "Checking #{@gem_repository}" Gem.use_paths @gem_repository.to_s unless gem_repository? say 'This directory does not appear to be a RubyGems repository, ' + 'skipping' say return end doctor_children say ensure Gem.use_paths @orig_home, *@orig_path end ## # Cleans up children of this gem repository def doctor_children # :nodoc: REPOSITORY_EXTENSION_MAP.each do |sub_directory, extension| doctor_child sub_directory, extension end end ## # Removes files in +sub_directory+ with +extension+ def doctor_child(sub_directory, extension) # :nodoc: directory = File.join(@gem_repository, sub_directory) Dir.entries(directory).sort.each do |ent| next if ent == "." || ent == ".." child = File.join(directory, ent) next unless File.exist?(child) basename = File.basename(child, extension) next if installed_specs.include? basename next if /^rubygems-\d/ =~ basename next if 'specifications' == sub_directory and 'default' == basename next if 'plugins' == sub_directory and Gem.plugin_suffix_regexp =~ basename type = File.directory?(child) ? 'directory' : 'file' action = if @dry_run 'Extra' else FileUtils.rm_r(child) 'Removed' end say "#{action} #{type} #{sub_directory}/#{File.basename(child)}" end rescue Errno::ENOENT # ignore end end rubygems/rubygems/safe_yaml.rb 0000644 00000003003 15102661023 0012513 0 ustar 00 module Gem ### # This module is used for safely loading YAML specs from a gem. The # `safe_load` method defined on this module is specifically designed for # loading Gem specifications. For loading other YAML safely, please see # Psych.safe_load module SafeYAML PERMITTED_CLASSES = %w[ Symbol Time Date Gem::Dependency Gem::Platform Gem::Requirement Gem::Specification Gem::Version Gem::Version::Requirement ].freeze PERMITTED_SYMBOLS = %w[ development runtime ].freeze if ::YAML.respond_to? :safe_load def self.safe_load(input) if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1') ::YAML.safe_load(input, permitted_classes: PERMITTED_CLASSES, permitted_symbols: PERMITTED_SYMBOLS, aliases: true) else ::YAML.safe_load(input, PERMITTED_CLASSES, PERMITTED_SYMBOLS, true) end end def self.load(input) if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1') ::YAML.safe_load(input, permitted_classes: [::Symbol]) else ::YAML.safe_load(input, [::Symbol]) end end else unless Gem::Deprecate.skip warn "YAML safe loading is not available. Please upgrade psych to a version that supports safe loading (>= 2.0)." end def self.safe_load(input, *args) ::YAML.load input end def self.load(input) ::YAML.load input end end end end rubygems/rubygems/config_file.rb 0000644 00000032441 15102661023 0013027 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'user_interaction' require 'rbconfig' ## # Gem::ConfigFile RubyGems options and gem command options from gemrc. # # gemrc is a YAML file that uses strings to match gem command arguments and # symbols to match RubyGems options. # # Gem command arguments use a String key that matches the command name and # allow you to specify default arguments: # # install: --no-rdoc --no-ri # update: --no-rdoc --no-ri # # You can use <tt>gem:</tt> to set default arguments for all commands. # # RubyGems options use symbol keys. Valid options are: # # +:backtrace+:: See #backtrace # +:sources+:: Sets Gem::sources # +:verbose+:: See #verbose # +:concurrent_downloads+:: See #concurrent_downloads # # gemrc files may exist in various locations and are read and merged in # the following order: # # - system wide (/etc/gemrc) # - per user (~/.gemrc) # - per environment (gemrc files listed in the GEMRC environment variable) class Gem::ConfigFile include Gem::UserInteraction DEFAULT_BACKTRACE = false DEFAULT_BULK_THRESHOLD = 1000 DEFAULT_VERBOSITY = true DEFAULT_UPDATE_SOURCES = true DEFAULT_CONCURRENT_DOWNLOADS = 8 DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365 DEFAULT_IPV4_FALLBACK_ENABLED = false ## # For Ruby packagers to set configuration defaults. Set in # rubygems/defaults/operating_system.rb OPERATING_SYSTEM_DEFAULTS = Gem.operating_system_defaults ## # For Ruby implementers to set configuration defaults. Set in # rubygems/defaults/#{RUBY_ENGINE}.rb PLATFORM_DEFAULTS = Gem.platform_defaults # :stopdoc: SYSTEM_CONFIG_PATH = begin require "etc" Etc.sysconfdir rescue LoadError, NoMethodError RbConfig::CONFIG["sysconfdir"] || "/etc" end # :startdoc: SYSTEM_WIDE_CONFIG_FILE = File.join SYSTEM_CONFIG_PATH, 'gemrc' ## # List of arguments supplied to the config file object. attr_reader :args ## # Where to look for gems (deprecated) attr_accessor :path ## # Where to install gems (deprecated) attr_accessor :home ## # True if we print backtraces on errors. attr_writer :backtrace ## # Bulk threshold value. If the number of missing gems are above this # threshold value, then a bulk download technique is used. (deprecated) attr_accessor :bulk_threshold ## # Verbose level of output: # * false -- No output # * true -- Normal output # * :loud -- Extra output attr_accessor :verbose ## # Number of gem downloads that should be performed concurrently. attr_accessor :concurrent_downloads ## # True if we want to update the SourceInfoCache every time, false otherwise attr_accessor :update_sources ## # True if we want to force specification of gem server when pushing a gem attr_accessor :disable_default_gem_server # openssl verify mode value, used for remote https connection attr_reader :ssl_verify_mode ## # Path name of directory or file of openssl CA certificate, used for remote # https connection attr_accessor :ssl_ca_cert ## # sources to look for gems attr_accessor :sources ## # Expiration length to sign a certificate attr_accessor :cert_expiration_length_days ## # == Experimental == # Fallback to IPv4 when IPv6 is not reachable or slow (default: false) attr_accessor :ipv4_fallback_enabled ## # Path name of directory or file of openssl client certificate, used for remote https connection with client authentication attr_reader :ssl_client_cert ## # Create the config file object. +args+ is the list of arguments # from the command line. # # The following command line options are handled early here rather # than later at the time most command options are processed. # # <tt>--config-file</tt>, <tt>--config-file==NAME</tt>:: # Obviously these need to be handled by the ConfigFile object to ensure we # get the right config file. # # <tt>--backtrace</tt>:: # Backtrace needs to be turned on early so that errors before normal # option parsing can be properly handled. # # <tt>--debug</tt>:: # Enable Ruby level debug messages. Handled early for the same reason as # --backtrace. #-- # TODO: parse options upstream, pass in options directly def initialize(args) set_config_file_name(args) @backtrace = DEFAULT_BACKTRACE @bulk_threshold = DEFAULT_BULK_THRESHOLD @verbose = DEFAULT_VERBOSITY @update_sources = DEFAULT_UPDATE_SOURCES @concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS @cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS @ipv4_fallback_enabled = ENV['IPV4_FALLBACK_ENABLED'] == 'true' || DEFAULT_IPV4_FALLBACK_ENABLED operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS) platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS) system_config = load_file SYSTEM_WIDE_CONFIG_FILE user_config = load_file config_file_name.dup.tap(&Gem::UNTAINT) environment_config = (ENV['GEMRC'] || '') .split(File::PATH_SEPARATOR).inject({}) do |result, file| result.merge load_file file end @hash = operating_system_config.merge platform_config unless args.index '--norc' @hash = @hash.merge system_config @hash = @hash.merge user_config @hash = @hash.merge environment_config end # HACK these override command-line args, which is bad @backtrace = @hash[:backtrace] if @hash.key? :backtrace @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold @home = @hash[:gemhome] if @hash.key? :gemhome @path = @hash[:gempath] if @hash.key? :gempath @update_sources = @hash[:update_sources] if @hash.key? :update_sources @verbose = @hash[:verbose] if @hash.key? :verbose @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server @sources = @hash[:sources] if @hash.key? :sources @cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days @ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert @ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert @api_keys = nil @rubygems_api_key = nil handle_arguments args end ## # Hash of RubyGems.org and alternate API keys def api_keys load_api_keys unless @api_keys @api_keys end ## # Checks the permissions of the credentials file. If they are not 0600 an # error message is displayed and RubyGems aborts. def check_credentials_permissions return if Gem.win_platform? # windows doesn't write 0600 as 0600 return unless File.exist? credentials_path existing_permissions = File.stat(credentials_path).mode & 0777 return if existing_permissions == 0600 alert_error <<-ERROR Your gem push credentials file located at: \t#{credentials_path} has file permissions of 0#{existing_permissions.to_s 8} but 0600 is required. To fix this error run: \tchmod 0600 #{credentials_path} You should reset your credentials at: \thttps://rubygems.org/profile/edit if you believe they were disclosed to a third party. ERROR terminate_interaction 1 end ## # Location of RubyGems.org credentials def credentials_path credentials = File.join Gem.user_home, '.gem', 'credentials' if File.exist? credentials credentials else File.join Gem.data_home, "gem", "credentials" end end def load_api_keys check_credentials_permissions @api_keys = if File.exist? credentials_path load_file(credentials_path) else @hash end if @api_keys.key? :rubygems_api_key @rubygems_api_key = @api_keys[:rubygems_api_key] @api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless @api_keys.key? :rubygems end end ## # Returns the RubyGems.org API key def rubygems_api_key load_api_keys unless @rubygems_api_key @rubygems_api_key end ## # Sets the RubyGems.org API key to +api_key+ def rubygems_api_key=(api_key) set_api_key :rubygems_api_key, api_key @rubygems_api_key = api_key end ## # Set a specific host's API key to +api_key+ def set_api_key(host, api_key) check_credentials_permissions config = load_file(credentials_path).merge(host => api_key) dirname = File.dirname credentials_path require 'fileutils' FileUtils.mkdir_p(dirname) Gem.load_yaml permissions = 0600 & (~File.umask) File.open(credentials_path, 'w', permissions) do |f| f.write config.to_yaml end load_api_keys # reload end ## # Remove the +~/.gem/credentials+ file to clear all the current sessions. def unset_api_key! return false unless File.exist?(credentials_path) File.delete(credentials_path) end def load_file(filename) Gem.load_yaml yaml_errors = [ArgumentError] yaml_errors << Psych::SyntaxError if defined?(Psych::SyntaxError) return {} unless filename && !filename.empty? && File.exist?(filename) begin content = Gem::SafeYAML.load(File.read(filename)) unless content.kind_of? Hash warn "Failed to load #{filename} because it doesn't contain valid YAML hash" return {} end return content rescue *yaml_errors => e warn "Failed to load #{filename}, #{e}" rescue Errno::EACCES warn "Failed to load #{filename} due to permissions problem." end {} end # True if the backtrace option has been specified, or debug is on. def backtrace @backtrace or $DEBUG end # The name of the configuration file. def config_file_name @config_file_name || Gem.config_file end # Delegates to @hash def each(&block) hash = @hash.dup hash.delete :update_sources hash.delete :verbose hash.delete :backtrace hash.delete :bulk_threshold yield :update_sources, @update_sources yield :verbose, @verbose yield :backtrace, @backtrace yield :bulk_threshold, @bulk_threshold yield 'config_file_name', @config_file_name if @config_file_name hash.each(&block) end # Handle the command arguments. def handle_arguments(arg_list) @args = [] arg_list.each do |arg| case arg when /^--(backtrace|traceback)$/ then @backtrace = true when /^--debug$/ then $DEBUG = true warn 'NOTE: Debugging mode prints all exceptions even when rescued' else @args << arg end end end # Really verbose mode gives you extra output. def really_verbose case verbose when true, false, nil then false else true end end # to_yaml only overwrites things you can't override on the command line. def to_yaml # :nodoc: yaml_hash = {} yaml_hash[:backtrace] = @hash.fetch(:backtrace, DEFAULT_BACKTRACE) yaml_hash[:bulk_threshold] = @hash.fetch(:bulk_threshold, DEFAULT_BULK_THRESHOLD) yaml_hash[:sources] = Gem.sources.to_a yaml_hash[:update_sources] = @hash.fetch(:update_sources, DEFAULT_UPDATE_SOURCES) yaml_hash[:verbose] = @hash.fetch(:verbose, DEFAULT_VERBOSITY) yaml_hash[:concurrent_downloads] = @hash.fetch(:concurrent_downloads, DEFAULT_CONCURRENT_DOWNLOADS) yaml_hash[:ssl_verify_mode] = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode yaml_hash[:ssl_ca_cert] = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert yaml_hash[:ssl_client_cert] = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert keys = yaml_hash.keys.map {|key| key.to_s } keys << 'debug' re = Regexp.union(*keys) @hash.each do |key, value| key = key.to_s next if key =~ re yaml_hash[key.to_s] = value end yaml_hash.to_yaml end # Writes out this config file, replacing its source. def write require 'fileutils' FileUtils.mkdir_p File.dirname(config_file_name) File.open config_file_name, 'w' do |io| io.write to_yaml end end # Return the configuration information for +key+. def [](key) @hash[key.to_s] end # Set configuration option +key+ to +value+. def []=(key, value) @hash[key.to_s] = value end def ==(other) # :nodoc: self.class === other and @backtrace == other.backtrace and @bulk_threshold == other.bulk_threshold and @verbose == other.verbose and @update_sources == other.update_sources and @hash == other.hash end attr_reader :hash protected :hash private def set_config_file_name(args) @config_file_name = ENV["GEMRC"] need_config_file_name = false args.each do |arg| if need_config_file_name @config_file_name = arg need_config_file_name = false elsif arg =~ /^--config-file=(.*)/ @config_file_name = $1 elsif arg =~ /^--config-file$/ need_config_file_name = true end end end end rubygems/rubygems/package.rb 0000644 00000042261 15102661023 0012157 0 ustar 00 # frozen_string_literal: true #-- # Copyright (C) 2004 Mauricio Julio Fernández Pradier # See LICENSE.txt for additional licensing information. #++ # # Example using a Gem::Package # # Builds a .gem file given a Gem::Specification. A .gem file is a tarball # which contains a data.tar.gz, metadata.gz, checksums.yaml.gz and possibly # signatures. # # require 'rubygems' # require 'rubygems/package' # # spec = Gem::Specification.new do |s| # s.summary = "Ruby based make-like utility." # s.name = 'rake' # s.version = PKG_VERSION # s.requirements << 'none' # s.files = PKG_FILES # s.description = <<-EOF # Rake is a Make-like program implemented in Ruby. Tasks # and dependencies are specified in standard Ruby syntax. # EOF # end # # Gem::Package.build spec # # Reads a .gem file. # # require 'rubygems' # require 'rubygems/package' # # the_gem = Gem::Package.new(path_to_dot_gem) # the_gem.contents # get the files in the gem # the_gem.extract_files destination_directory # extract the gem into a directory # the_gem.spec # get the spec out of the gem # the_gem.verify # check the gem is OK (contains valid gem specification, contains a not corrupt contents archive) # # #files are the files in the .gem tar file, not the Ruby files in the gem # #extract_files and #contents automatically call #verify require_relative "../rubygems" require_relative 'security' require_relative 'user_interaction' class Gem::Package include Gem::UserInteraction class Error < Gem::Exception; end class FormatError < Error attr_reader :path def initialize(message, source = nil) if source @path = source.path message = message + " in #{path}" if path end super message end end class PathError < Error def initialize(destination, destination_dir) super "installing into parent path %s of %s is not allowed" % [destination, destination_dir] end end class SymlinkError < Error def initialize(name, destination, destination_dir) super "installing symlink '%s' pointing to parent path %s of %s is not allowed" % [name, destination, destination_dir] end end class NonSeekableIO < Error; end class TooLongFileName < Error; end ## # Raised when a tar file is corrupt class TarInvalidError < Error; end attr_accessor :build_time # :nodoc: ## # Checksums for the contents of the package attr_reader :checksums ## # The files in this package. This is not the contents of the gem, just the # files in the top-level container. attr_reader :files ## # Reference to the gem being packaged. attr_reader :gem ## # The security policy used for verifying the contents of this package. attr_accessor :security_policy ## # Sets the Gem::Specification to use to build this package. attr_writer :spec ## # Permission for directories attr_accessor :dir_mode ## # Permission for program files attr_accessor :prog_mode ## # Permission for other files attr_accessor :data_mode def self.build(spec, skip_validation = false, strict_validation = false, file_name = nil) gem_file = file_name || spec.file_name package = new gem_file package.spec = spec package.build skip_validation, strict_validation gem_file end ## # Creates a new Gem::Package for the file at +gem+. +gem+ can also be # provided as an IO object. # # If +gem+ is an existing file in the old format a Gem::Package::Old will be # returned. def self.new(gem, security_policy = nil) gem = if gem.is_a?(Gem::Package::Source) gem elsif gem.respond_to? :read Gem::Package::IOSource.new gem else Gem::Package::FileSource.new gem end return super unless Gem::Package == self return super unless gem.present? return super unless gem.start return super unless gem.start.include? 'MD5SUM =' Gem::Package::Old.new gem end ## # Extracts the Gem::Specification and raw metadata from the .gem file at # +path+. #-- def self.raw_spec(path, security_policy = nil) format = new(path, security_policy) spec = format.spec metadata = nil File.open path, Gem.binary_mode do |io| tar = Gem::Package::TarReader.new io tar.each_entry do |entry| case entry.full_name when 'metadata' then metadata = entry.read when 'metadata.gz' then metadata = Gem::Util.gunzip entry.read end end end return spec, metadata end ## # Creates a new package that will read or write to the file +gem+. def initialize(gem, security_policy) # :notnew: require 'zlib' @gem = gem @build_time = Gem.source_date_epoch @checksums = {} @contents = nil @digests = Hash.new {|h, algorithm| h[algorithm] = {} } @files = nil @security_policy = security_policy @signatures = {} @signer = nil @spec = nil end ## # Copies this package to +path+ (if possible) def copy_to(path) FileUtils.cp @gem.path, path unless File.exist? path end ## # Adds a checksum for each entry in the gem to checksums.yaml.gz. def add_checksums(tar) Gem.load_yaml checksums_by_algorithm = Hash.new {|h, algorithm| h[algorithm] = {} } @checksums.each do |name, digests| digests.each do |algorithm, digest| checksums_by_algorithm[algorithm][name] = digest.hexdigest end end tar.add_file_signed 'checksums.yaml.gz', 0444, @signer do |io| gzip_to io do |gz_io| YAML.dump checksums_by_algorithm, gz_io end end end ## # Adds the files listed in the packages's Gem::Specification to data.tar.gz # and adds this file to the +tar+. def add_contents(tar) # :nodoc: digests = tar.add_file_signed 'data.tar.gz', 0444, @signer do |io| gzip_to io do |gz_io| Gem::Package::TarWriter.new gz_io do |data_tar| add_files data_tar end end end @checksums['data.tar.gz'] = digests end ## # Adds files included the package's Gem::Specification to the +tar+ file def add_files(tar) # :nodoc: @spec.files.each do |file| stat = File.lstat file if stat.symlink? tar.add_symlink file, File.readlink(file), stat.mode end next unless stat.file? tar.add_file_simple file, stat.mode, stat.size do |dst_io| File.open file, 'rb' do |src_io| dst_io.write src_io.read 16384 until src_io.eof? end end end end ## # Adds the package's Gem::Specification to the +tar+ file def add_metadata(tar) # :nodoc: digests = tar.add_file_signed 'metadata.gz', 0444, @signer do |io| gzip_to io do |gz_io| gz_io.write @spec.to_yaml end end @checksums['metadata.gz'] = digests end ## # Builds this package based on the specification set by #spec= def build(skip_validation = false, strict_validation = false) raise ArgumentError, "skip_validation = true and strict_validation = true are incompatible" if skip_validation && strict_validation Gem.load_yaml @spec.mark_version @spec.validate true, strict_validation unless skip_validation setup_signer( signer_options: { expiration_length_days: Gem.configuration.cert_expiration_length_days, } ) @gem.with_write_io do |gem_io| Gem::Package::TarWriter.new gem_io do |gem| add_metadata gem add_contents gem add_checksums gem end end say <<-EOM Successfully built RubyGem Name: #{@spec.name} Version: #{@spec.version} File: #{File.basename @gem.path} EOM ensure @signer = nil end ## # A list of file names contained in this gem def contents return @contents if @contents verify unless @spec @contents = [] @gem.with_read_io do |io| gem_tar = Gem::Package::TarReader.new io gem_tar.each do |entry| next unless entry.full_name == 'data.tar.gz' open_tar_gz entry do |pkg_tar| pkg_tar.each do |contents_entry| @contents << contents_entry.full_name end end return @contents end end end ## # Creates a digest of the TarEntry +entry+ from the digest algorithm set by # the security policy. def digest(entry) # :nodoc: algorithms = if @checksums @checksums.keys else [Gem::Security::DIGEST_NAME].compact end algorithms.each do |algorithm| digester = Gem::Security.create_digest(algorithm) digester << entry.read(16384) until entry.eof? entry.rewind @digests[algorithm][entry.full_name] = digester end @digests end ## # Extracts the files in this package into +destination_dir+ # # If +pattern+ is specified, only entries matching that glob will be # extracted. def extract_files(destination_dir, pattern = "*") verify unless @spec FileUtils.mkdir_p destination_dir, :mode => dir_mode && 0755 @gem.with_read_io do |io| reader = Gem::Package::TarReader.new io reader.each do |entry| next unless entry.full_name == 'data.tar.gz' extract_tar_gz entry, destination_dir, pattern return # ignore further entries end end end ## # Extracts all the files in the gzipped tar archive +io+ into # +destination_dir+. # # If an entry in the archive contains a relative path above # +destination_dir+ or an absolute path is encountered an exception is # raised. # # If +pattern+ is specified, only entries matching that glob will be # extracted. def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc: directories = [] open_tar_gz io do |tar| tar.each do |entry| next unless File.fnmatch pattern, entry.full_name, File::FNM_DOTMATCH destination = install_location entry.full_name, destination_dir if entry.symlink? link_target = entry.header.linkname real_destination = link_target.start_with?("/") ? link_target : File.expand_path(link_target, File.dirname(destination)) raise Gem::Package::SymlinkError.new(entry.full_name, real_destination, destination_dir) unless normalize_path(real_destination).start_with? normalize_path(destination_dir + '/') end FileUtils.rm_rf destination mkdir_options = {} mkdir_options[:mode] = dir_mode ? 0755 : (entry.header.mode if entry.directory?) mkdir = if entry.directory? destination else File.dirname destination end unless directories.include?(mkdir) FileUtils.mkdir_p mkdir, **mkdir_options directories << mkdir end File.open destination, 'wb' do |out| out.write entry.read FileUtils.chmod file_mode(entry.header.mode), destination end if entry.file? File.symlink(entry.header.linkname, destination) if entry.symlink? verbose destination end end if dir_mode File.chmod(dir_mode, *directories) end end def file_mode(mode) # :nodoc: ((mode & 0111).zero? ? data_mode : prog_mode) || mode end ## # Gzips content written to +gz_io+ to +io+. #-- # Also sets the gzip modification time to the package build time to ease # testing. def gzip_to(io) # :yields: gz_io gz_io = Zlib::GzipWriter.new io, Zlib::BEST_COMPRESSION gz_io.mtime = @build_time yield gz_io ensure gz_io.close end ## # Returns the full path for installing +filename+. # # If +filename+ is not inside +destination_dir+ an exception is raised. def install_location(filename, destination_dir) # :nodoc: raise Gem::Package::PathError.new(filename, destination_dir) if filename.start_with? '/' destination_dir = File.realpath(destination_dir) destination = File.expand_path(filename, destination_dir) raise Gem::Package::PathError.new(destination, destination_dir) unless normalize_path(destination).start_with? normalize_path(destination_dir + '/') destination.tap(&Gem::UNTAINT) destination end def normalize_path(pathname) if Gem.win_platform? pathname.downcase else pathname end end ## # Loads a Gem::Specification from the TarEntry +entry+ def load_spec(entry) # :nodoc: case entry.full_name when 'metadata' then @spec = Gem::Specification.from_yaml entry.read when 'metadata.gz' then Zlib::GzipReader.wrap(entry, external_encoding: Encoding::UTF_8) do |gzio| @spec = Gem::Specification.from_yaml gzio.read end end end ## # Opens +io+ as a gzipped tar archive def open_tar_gz(io) # :nodoc: Zlib::GzipReader.wrap io do |gzio| tar = Gem::Package::TarReader.new gzio yield tar end end ## # Reads and loads checksums.yaml.gz from the tar file +gem+ def read_checksums(gem) Gem.load_yaml @checksums = gem.seek 'checksums.yaml.gz' do |entry| Zlib::GzipReader.wrap entry do |gz_io| Gem::SafeYAML.safe_load gz_io.read end end end ## # Prepares the gem for signing and checksum generation. If a signing # certificate and key are not present only checksum generation is set up. def setup_signer(signer_options: {}) passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE'] if @spec.signing_key @signer = Gem::Security::Signer.new( @spec.signing_key, @spec.cert_chain, passphrase, signer_options ) @spec.signing_key = nil @spec.cert_chain = @signer.cert_chain.map {|cert| cert.to_s } else @signer = Gem::Security::Signer.new nil, nil, passphrase @spec.cert_chain = @signer.cert_chain.map {|cert| cert.to_pem } if @signer.cert_chain end end ## # The spec for this gem. # # If this is a package for a built gem the spec is loaded from the # gem and returned. If this is a package for a gem being built the provided # spec is returned. def spec verify unless @spec @spec end ## # Verifies that this gem: # # * Contains a valid gem specification # * Contains a contents archive # * The contents archive is not corrupt # # After verification the gem specification from the gem is available from # #spec def verify @files = [] @spec = nil @gem.with_read_io do |io| Gem::Package::TarReader.new io do |reader| read_checksums reader verify_files reader end end verify_checksums @digests, @checksums @security_policy.verify_signatures @spec, @digests, @signatures if @security_policy true rescue Gem::Security::Exception @spec = nil @files = [] raise rescue Errno::ENOENT => e raise Gem::Package::FormatError.new e.message rescue Gem::Package::TarInvalidError => e raise Gem::Package::FormatError.new e.message, @gem end ## # Verifies the +checksums+ against the +digests+. This check is not # cryptographically secure. Missing checksums are ignored. def verify_checksums(digests, checksums) # :nodoc: return unless checksums checksums.sort.each do |algorithm, gem_digests| gem_digests.sort.each do |file_name, gem_hexdigest| computed_digest = digests[algorithm][file_name] unless computed_digest.hexdigest == gem_hexdigest raise Gem::Package::FormatError.new \ "#{algorithm} checksum mismatch for #{file_name}", @gem end end end end ## # Verifies +entry+ in a .gem file. def verify_entry(entry) file_name = entry.full_name @files << file_name case file_name when /\.sig$/ then @signatures[$`] = entry.read if @security_policy return else digest entry end case file_name when "metadata", "metadata.gz" then load_spec entry when 'data.tar.gz' then verify_gz entry end rescue warn "Exception while verifying #{@gem.path}" raise end ## # Verifies the files of the +gem+ def verify_files(gem) gem.each do |entry| verify_entry entry end unless @spec raise Gem::Package::FormatError.new 'package metadata is missing', @gem end unless @files.include? 'data.tar.gz' raise Gem::Package::FormatError.new \ 'package content (data.tar.gz) is missing', @gem end if duplicates = @files.group_by {|f| f }.select {|k,v| v.size > 1 }.map(&:first) and duplicates.any? raise Gem::Security::Exception, "duplicate files in the package: (#{duplicates.map(&:inspect).join(', ')})" end end ## # Verifies that +entry+ is a valid gzipped file. def verify_gz(entry) # :nodoc: Zlib::GzipReader.wrap entry do |gzio| gzio.read 16384 until gzio.eof? # gzip checksum verification end rescue Zlib::GzipFile::Error => e raise Gem::Package::FormatError.new(e.message, entry.full_name) end end require_relative 'package/digest_io' require_relative 'package/source' require_relative 'package/file_source' require_relative 'package/io_source' require_relative 'package/old' require_relative 'package/tar_header' require_relative 'package/tar_reader' require_relative 'package/tar_reader/entry' require_relative 'package/tar_writer' rubygems/rubygems/command_manager.rb 0000644 00000012140 15102661023 0013665 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'command' require_relative 'user_interaction' require_relative 'text' ## # The command manager registers and installs all the individual sub-commands # supported by the gem command. # # Extra commands can be provided by writing a rubygems_plugin.rb # file in an installed gem. You should register your command against the # Gem::CommandManager instance, like this: # # # file rubygems_plugin.rb # require 'rubygems/command_manager' # # Gem::CommandManager.instance.register_command :edit # # You should put the implementation of your command in rubygems/commands. # # # file rubygems/commands/edit_command.rb # class Gem::Commands::EditCommand < Gem::Command # # ... # end # # See Gem::Command for instructions on writing gem commands. class Gem::CommandManager include Gem::Text include Gem::UserInteraction BUILTIN_COMMANDS = [ # :nodoc: :build, :cert, :check, :cleanup, :contents, :dependency, :environment, :fetch, :generate_index, :help, :info, :install, :list, :lock, :mirror, :open, :outdated, :owner, :pristine, :push, :query, :rdoc, :search, :server, :signin, :signout, :sources, :specification, :stale, :uninstall, :unpack, :update, :which, :yank, ].freeze ALIAS_COMMANDS = { 'i' => 'install', 'login' => 'signin', 'logout' => 'signout', }.freeze ## # Return the authoritative instance of the command manager. def self.instance @command_manager ||= new end ## # Returns self. Allows a CommandManager instance to stand # in for the class itself. def instance self end ## # Reset the authoritative instance of the command manager. def self.reset @command_manager = nil end ## # Register all the subcommands supported by the gem command. def initialize require 'timeout' @commands = {} BUILTIN_COMMANDS.each do |name| register_command name end end ## # Register the Symbol +command+ as a gem command. def register_command(command, obj=false) @commands[command] = obj end ## # Unregister the Symbol +command+ as a gem command. def unregister_command(command) @commands.delete command end ## # Returns a Command instance for +command_name+ def [](command_name) command_name = command_name.intern return nil if @commands[command_name].nil? @commands[command_name] ||= load_and_instantiate(command_name) end ## # Return a sorted list of all command names as strings. def command_names @commands.keys.collect {|key| key.to_s }.sort end ## # Run the command specified by +args+. def run(args, build_args=nil) process_args(args, build_args) rescue StandardError, Timeout::Error => ex alert_error clean_text("While executing gem ... (#{ex.class})\n #{ex}") ui.backtrace ex terminate_interaction(1) rescue Interrupt alert_error clean_text("Interrupted") terminate_interaction(1) end def process_args(args, build_args=nil) if args.empty? say Gem::Command::HELP terminate_interaction 1 end case args.first when '-h', '--help' then say Gem::Command::HELP terminate_interaction 0 when '-v', '--version' then say Gem::VERSION terminate_interaction 0 when /^-/ then alert_error clean_text("Invalid option: #{args.first}. See 'gem --help'.") terminate_interaction 1 else cmd_name = args.shift.downcase cmd = find_command cmd_name cmd.deprecation_warning if cmd.deprecated? cmd.invoke_with_build_args args, build_args end end def find_command(cmd_name) cmd_name = find_alias_command cmd_name possibilities = find_command_possibilities cmd_name if possibilities.size > 1 raise Gem::CommandLineError, "Ambiguous command #{cmd_name} matches [#{possibilities.join(', ')}]" elsif possibilities.empty? raise Gem::CommandLineError, "Unknown command #{cmd_name}" end self[possibilities.first] end def find_alias_command(cmd_name) alias_name = ALIAS_COMMANDS[cmd_name] alias_name ? alias_name : cmd_name end def find_command_possibilities(cmd_name) len = cmd_name.length found = command_names.select {|name| cmd_name == name[0, len] } exact = found.find {|name| name == cmd_name } exact ? [exact] : found end private def load_and_instantiate(command_name) command_name = command_name.to_s const_name = command_name.capitalize.gsub(/_(.)/) { $1.upcase } << "Command" load_error = nil begin begin require "rubygems/commands/#{command_name}_command" rescue LoadError => e load_error = e end Gem::Commands.const_get(const_name).new rescue Exception => e e = load_error if load_error alert_error clean_text("Loading command: #{command_name} (#{e.class})\n\t#{e}") ui.backtrace e end end end rubygems/rubygems/install_message.rb 0000644 00000000502 15102661023 0013726 0 ustar 00 # frozen_string_literal: true require_relative '../rubygems' require_relative 'user_interaction' ## # A default post-install hook that displays "Successfully installed # some_gem-1.0" Gem.post_install do |installer| ui = Gem::DefaultUserInteraction.ui ui.say "Successfully installed #{installer.spec.full_name}" end rubygems/rubygems/validator.rb 0000644 00000007230 15102661023 0012546 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'package' require_relative 'installer' ## # Validator performs various gem file and gem database validation class Gem::Validator include Gem::UserInteraction def initialize # :nodoc: require 'find' end private def find_files_for_gem(gem_directory) installed_files = [] Find.find gem_directory do |file_name| fn = file_name[gem_directory.size..file_name.size - 1].sub(/^\//, "") installed_files << fn unless fn =~ /CVS/ || fn.empty? || File.directory?(file_name) end installed_files end public ## # Describes a problem with a file in a gem. ErrorData = Struct.new :path, :problem do def <=>(other) # :nodoc: return nil unless self.class === other [path, problem] <=> [other.path, other.problem] end end ## # Checks the gem directory for the following potential # inconsistencies/problems: # # * Checksum gem itself # * For each file in each gem, check consistency of installed versions # * Check for files that aren't part of the gem but are in the gems directory # * 1 cache - 1 spec - 1 directory. # # returns a hash of ErrorData objects, keyed on the problem gem's name. #-- # TODO needs further cleanup def alien(gems=[]) errors = Hash.new {|h,k| h[k] = {} } Gem::Specification.each do |spec| next unless gems.include? spec.name unless gems.empty? next if spec.default_gem? gem_name = spec.file_name gem_path = spec.cache_file spec_path = spec.spec_file gem_directory = spec.full_gem_path unless File.directory? gem_directory errors[gem_name][spec.full_name] = "Gem registered but doesn't exist at #{gem_directory}" next end unless File.exist? spec_path errors[gem_name][spec_path] = "Spec file missing for installed gem" end begin unless File.readable?(gem_path) raise Gem::VerificationError, "missing gem file #{gem_path}" end good, gone, unreadable = nil, nil, nil, nil File.open gem_path, Gem.binary_mode do |file| package = Gem::Package.new gem_path good, gone = package.contents.partition do |file_name| File.exist? File.join(gem_directory, file_name) end gone.sort.each do |path| errors[gem_name][path] = "Missing file" end good, unreadable = good.partition do |file_name| File.readable? File.join(gem_directory, file_name) end unreadable.sort.each do |path| errors[gem_name][path] = "Unreadable file" end good.each do |entry, data| begin next unless data # HACK `gem check -a mkrf` source = File.join gem_directory, entry['path'] File.open source, Gem.binary_mode do |f| unless f.read == data errors[gem_name][entry['path']] = "Modified from original" end end end end end installed_files = find_files_for_gem(gem_directory) extras = installed_files - good - unreadable extras.each do |extra| errors[gem_name][extra] = "Extra file" end rescue Gem::VerificationError => e errors[gem_name][gem_path] = e.message end end errors.each do |name, subhash| errors[name] = subhash.map do |path, msg| ErrorData.new path, msg end.sort end errors end end rubygems/rubygems/uninstaller.rb 0000644 00000024344 15102661023 0013126 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require 'fileutils' require_relative '../rubygems' require_relative 'installer_uninstaller_utils' require_relative 'dependency_list' require_relative 'rdoc' require_relative 'user_interaction' ## # An Uninstaller. # # The uninstaller fires pre and post uninstall hooks. Hooks can be added # either through a rubygems_plugin.rb file in an installed gem or via a # rubygems/defaults/#{RUBY_ENGINE}.rb or rubygems/defaults/operating_system.rb # file. See Gem.pre_uninstall and Gem.post_uninstall for details. class Gem::Uninstaller include Gem::UserInteraction include Gem::InstallerUninstallerUtils ## # The directory a gem's executables will be installed into attr_reader :bin_dir ## # The gem repository the gem will be installed into attr_reader :gem_home ## # The Gem::Specification for the gem being uninstalled, only set during # #uninstall_gem attr_reader :spec ## # Constructs an uninstaller that will uninstall +gem+ def initialize(gem, options = {}) # TODO document the valid options @gem = gem @version = options[:version] || Gem::Requirement.default @gem_home = File.realpath(options[:install_dir] || Gem.dir) @plugins_dir = Gem.plugindir(@gem_home) @force_executables = options[:executables] @force_all = options[:all] @force_ignore = options[:ignore] @bin_dir = options[:bin_dir] @format_executable = options[:format_executable] @abort_on_dependent = options[:abort_on_dependent] # Indicate if development dependencies should be checked when # uninstalling. (default: false) # @check_dev = options[:check_dev] if options[:force] @force_all = true @force_ignore = true end # only add user directory if install_dir is not set @user_install = false @user_install = options[:user_install] unless options[:install_dir] # Optimization: populated during #uninstall @default_specs_matching_uninstall_params = [] end ## # Performs the uninstall of the gem. This removes the spec, the Gem # directory, and the cached .gem file. def uninstall dependency = Gem::Dependency.new @gem, @version list = [] dirs = Gem::Specification.dirs + [Gem.default_specifications_dir] Gem::Specification.each_spec dirs do |spec| next unless dependency.matches_spec? spec list << spec end if list.empty? raise Gem::InstallError, "gem #{@gem.inspect} is not installed" end default_specs, list = list.partition do |spec| spec.default_gem? end warn_cannot_uninstall_default_gems(default_specs - list) @default_specs_matching_uninstall_params = default_specs list, other_repo_specs = list.partition do |spec| @gem_home == spec.base_dir or (@user_install and spec.base_dir == Gem.user_dir) end list.sort! if list.empty? return unless other_repo_specs.any? other_repos = other_repo_specs.map {|spec| spec.base_dir }.uniq message = ["#{@gem} is not installed in GEM_HOME, try:"] message.concat other_repos.map {|repo| "\tgem uninstall -i #{repo} #{@gem}" } raise Gem::InstallError, message.join("\n") elsif @force_all remove_all list elsif list.size > 1 gem_names = list.map {|gem| gem.full_name } gem_names << "All versions" say _, index = choose_from_list "Select gem to uninstall:", gem_names if index == list.size remove_all list elsif index >= 0 && index < list.size uninstall_gem list[index] else say "Error: must enter a number [1-#{list.size + 1}]" end else uninstall_gem list.first end end ## # Uninstalls gem +spec+ def uninstall_gem(spec) @spec = spec unless dependencies_ok? spec if abort_on_dependent? || !ask_if_ok(spec) raise Gem::DependencyRemovalException, "Uninstallation aborted due to dependent gem(s)" end end Gem.pre_uninstall_hooks.each do |hook| hook.call self end remove_executables @spec remove_plugins @spec remove @spec regenerate_plugins Gem.post_uninstall_hooks.each do |hook| hook.call self end @spec = nil end ## # Removes installed executables and batch files (windows only) for +spec+. def remove_executables(spec) return if spec.executables.empty? executables = spec.executables.clone # Leave any executables created by other installed versions # of this gem installed. list = Gem::Specification.find_all do |s| s.name == spec.name && s.version != spec.version end list.each do |s| s.executables.each do |exe_name| executables.delete exe_name end end return if executables.empty? executables = executables.map {|exec| formatted_program_filename exec } remove = if @force_executables.nil? ask_yes_no("Remove executables:\n" + "\t#{executables.join ', '}\n\n" + "in addition to the gem?", true) else @force_executables end if remove bin_dir = @bin_dir || Gem.bindir(spec.base_dir) raise Gem::FilePermissionError, bin_dir unless File.writable? bin_dir executables.each do |exe_name| say "Removing #{exe_name}" exe_file = File.join bin_dir, exe_name safe_delete { FileUtils.rm exe_file } safe_delete { FileUtils.rm "#{exe_file}.bat" } end else say "Executables and scripts will remain installed." end end ## # Removes all gems in +list+. # # NOTE: removes uninstalled gems from +list+. def remove_all(list) list.each {|spec| uninstall_gem spec } end ## # spec:: the spec of the gem to be uninstalled def remove(spec) unless path_ok?(@gem_home, spec) or (@user_install and path_ok?(Gem.user_dir, spec)) e = Gem::GemNotInHomeException.new \ "Gem '#{spec.full_name}' is not installed in directory #{@gem_home}" e.spec = spec raise e end raise Gem::FilePermissionError, spec.base_dir unless File.writable?(spec.base_dir) safe_delete { FileUtils.rm_r spec.full_gem_path } safe_delete { FileUtils.rm_r spec.extension_dir } old_platform_name = spec.original_name gem = spec.cache_file gem = File.join(spec.cache_dir, "#{old_platform_name}.gem") unless File.exist? gem safe_delete { FileUtils.rm_r gem } Gem::RDoc.new(spec).remove gemspec = spec.spec_file unless File.exist? gemspec gemspec = File.join(File.dirname(gemspec), "#{old_platform_name}.gemspec") end safe_delete { FileUtils.rm_r gemspec } announce_deletion_of(spec) Gem::Specification.reset end ## # Remove any plugin wrappers for +spec+. def remove_plugins(spec) # :nodoc: return if spec.plugins.empty? remove_plugins_for(spec, @plugins_dir) end ## # Regenerates plugin wrappers after removal. def regenerate_plugins latest = Gem::Specification.latest_spec_for(@spec.name) return if latest.nil? regenerate_plugins_for(latest, @plugins_dir) end ## # Is +spec+ in +gem_dir+? def path_ok?(gem_dir, spec) full_path = File.join gem_dir, 'gems', spec.full_name original_path = File.join gem_dir, 'gems', spec.original_name full_path == spec.full_gem_path || original_path == spec.full_gem_path end ## # Returns true if it is OK to remove +spec+ or this is a forced # uninstallation. def dependencies_ok?(spec) # :nodoc: return true if @force_ignore deplist = Gem::DependencyList.from_specs deplist.ok_to_remove?(spec.full_name, @check_dev) end ## # Should the uninstallation abort if a dependency will go unsatisfied? # # See ::new. def abort_on_dependent? # :nodoc: @abort_on_dependent end ## # Asks if it is OK to remove +spec+. Returns true if it is OK. def ask_if_ok(spec) # :nodoc: msg = [''] msg << 'You have requested to uninstall the gem:' msg << "\t#{spec.full_name}" msg << '' siblings = Gem::Specification.select do |s| s.name == spec.name && s.full_name != spec.full_name end spec.dependent_gems(@check_dev).each do |dep_spec, dep, satlist| unless siblings.any? {|s| s.satisfies_requirement? dep } msg << "#{dep_spec.name}-#{dep_spec.version} depends on #{dep}" end end msg << 'If you remove this gem, these dependencies will not be met.' msg << 'Continue with Uninstall?' return ask_yes_no(msg.join("\n"), false) end ## # Returns the formatted version of the executable +filename+ def formatted_program_filename(filename) # :nodoc: # TODO perhaps the installer should leave a small manifest # of what it did for us to find rather than trying to recreate # it again. if @format_executable require_relative 'installer' Gem::Installer.exec_format % File.basename(filename) else filename end end def safe_delete(&block) block.call rescue Errno::ENOENT nil rescue Errno::EPERM e = Gem::UninstallError.new e.spec = @spec raise e end private def announce_deletion_of(spec) name = spec.full_name say "Successfully uninstalled #{name}" if default_spec_matches?(spec) say( "There was both a regular copy and a default copy of #{name}. The " \ "regular copy was successfully uninstalled, but the default copy " \ "was left around because default gems can't be removed." ) end end # @return true if the specs of any default gems are `==` to the given `spec`. def default_spec_matches?(spec) !default_specs_that_match(spec).empty? end # @return [Array] specs of default gems that are `==` to the given `spec`. def default_specs_that_match(spec) @default_specs_matching_uninstall_params.select {|default_spec| spec == default_spec } end def warn_cannot_uninstall_default_gems(specs) specs.each do |spec| say "Gem #{spec.full_name} cannot be uninstalled because it is a default gem" end end end rubygems/rubygems/stub_specification.rb 0000644 00000011366 15102661023 0014443 0 ustar 00 # frozen_string_literal: true ## # Gem::StubSpecification reads the stub: line from the gemspec. This prevents # us having to eval the entire gemspec in order to find out certain # information. class Gem::StubSpecification < Gem::BasicSpecification # :nodoc: PREFIX = "# stub: ".freeze # :nodoc: OPEN_MODE = 'r:UTF-8:-'.freeze class StubLine # :nodoc: all attr_reader :name, :version, :platform, :require_paths, :extensions, :full_name NO_EXTENSIONS = [].freeze # These are common require paths. REQUIRE_PATHS = { # :nodoc: 'lib' => 'lib'.freeze, 'test' => 'test'.freeze, 'ext' => 'ext'.freeze, }.freeze # These are common require path lists. This hash is used to optimize # and consolidate require_path objects. Most specs just specify "lib" # in their require paths, so lets take advantage of that by pre-allocating # a require path list for that case. REQUIRE_PATH_LIST = { # :nodoc: 'lib' => ['lib'].freeze, }.freeze def initialize(data, extensions) parts = data[PREFIX.length..-1].split(" ".freeze, 4) @name = parts[0].freeze @version = if Gem::Version.correct?(parts[1]) Gem::Version.new(parts[1]) else Gem::Version.new(0) end @platform = Gem::Platform.new parts[2] @extensions = extensions @full_name = if platform == Gem::Platform::RUBY "#{name}-#{version}" else "#{name}-#{version}-#{platform}" end path_list = parts.last @require_paths = REQUIRE_PATH_LIST[path_list] || path_list.split("\0".freeze).map! do |x| REQUIRE_PATHS[x] || x end end end def self.default_gemspec_stub(filename, base_dir, gems_dir) new filename, base_dir, gems_dir, true end def self.gemspec_stub(filename, base_dir, gems_dir) new filename, base_dir, gems_dir, false end attr_reader :base_dir, :gems_dir def initialize(filename, base_dir, gems_dir, default_gem) super() filename.tap(&Gem::UNTAINT) self.loaded_from = filename @data = nil @name = nil @spec = nil @base_dir = base_dir @gems_dir = gems_dir @default_gem = default_gem end ## # True when this gem has been activated def activated? @activated ||= begin loaded = Gem.loaded_specs[name] loaded && loaded.version == version end end def default_gem? @default_gem end def build_extensions # :nodoc: return if default_gem? return if extensions.empty? to_spec.build_extensions end ## # If the gemspec contains a stubline, returns a StubLine instance. Otherwise # returns the full Gem::Specification. def data unless @data begin saved_lineno = $. File.open loaded_from, OPEN_MODE do |file| begin file.readline # discard encoding line stubline = file.readline.chomp if stubline.start_with?(PREFIX) extensions = if /\A#{PREFIX}/ =~ file.readline.chomp $'.split "\0" else StubLine::NO_EXTENSIONS end @data = StubLine.new stubline, extensions end rescue EOFError end end ensure $. = saved_lineno end end @data ||= to_spec end private :data def raw_require_paths # :nodoc: data.require_paths end def missing_extensions? return false if default_gem? return false if extensions.empty? return false if File.exist? gem_build_complete_path to_spec.missing_extensions? end ## # Name of the gem def name data.name end ## # Platform of the gem def platform data.platform end ## # Extensions for this gem def extensions data.extensions end ## # Version of the gem def version data.version end def full_name data.full_name end ## # The full Gem::Specification for this gem, loaded from evalling its gemspec def to_spec @spec ||= if @data loaded = Gem.loaded_specs[name] loaded if loaded && loaded.version == version end @spec ||= Gem::Specification.load(loaded_from) @spec.ignored = @ignored if @spec @spec end ## # Is this StubSpecification valid? i.e. have we found a stub line, OR does # the filename contain a valid gemspec? def valid? data end ## # Is there a stub line present for this StubSpecification? def stubbed? data.is_a? StubLine end end rubygems/rubygems/resolver.rb 0000644 00000023226 15102661023 0012425 0 ustar 00 # frozen_string_literal: true require_relative 'dependency' require_relative 'exceptions' require_relative 'util/list' ## # Given a set of Gem::Dependency objects as +needed+ and a way to query the # set of available specs via +set+, calculates a set of ActivationRequest # objects which indicate all the specs that should be activated to meet the # all the requirements. class Gem::Resolver require_relative 'resolver/molinillo' ## # If the DEBUG_RESOLVER environment variable is set then debugging mode is # enabled for the resolver. This will display information about the state # of the resolver while a set of dependencies is being resolved. DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil? ## # Set to true if all development dependencies should be considered. attr_accessor :development ## # Set to true if immediate development dependencies should be considered. attr_accessor :development_shallow ## # When true, no dependencies are looked up for requested gems. attr_accessor :ignore_dependencies ## # List of dependencies that could not be found in the configured sources. attr_reader :missing attr_reader :stats ## # Hash of gems to skip resolution. Keyed by gem name, with arrays of # gem specifications as values. attr_accessor :skip_gems ## # When a missing dependency, don't stop. Just go on and record what was # missing. attr_accessor :soft_missing ## # Combines +sets+ into a ComposedSet that allows specification lookup in a # uniform manner. If one of the +sets+ is itself a ComposedSet its sets are # flattened into the result ComposedSet. def self.compose_sets(*sets) sets.compact! sets = sets.map do |set| case set when Gem::Resolver::BestSet then set when Gem::Resolver::ComposedSet then set.sets else set end end.flatten case sets.length when 0 then raise ArgumentError, 'one set in the composition must be non-nil' when 1 then sets.first else Gem::Resolver::ComposedSet.new(*sets) end end ## # Creates a Resolver that queries only against the already installed gems # for the +needed+ dependencies. def self.for_current_gems(needed) new needed, Gem::Resolver::CurrentSet.new end ## # Create Resolver object which will resolve the tree starting # with +needed+ Dependency objects. # # +set+ is an object that provides where to look for specifications to # satisfy the Dependencies. This defaults to IndexSet, which will query # rubygems.org. def initialize(needed, set = nil) @set = set || Gem::Resolver::IndexSet.new @needed = needed @development = false @development_shallow = false @ignore_dependencies = false @missing = [] @skip_gems = {} @soft_missing = false @stats = Gem::Resolver::Stats.new end def explain(stage, *data) # :nodoc: return unless DEBUG_RESOLVER d = data.map {|x| x.pretty_inspect }.join(", ") $stderr.printf "%10s %s\n", stage.to_s.upcase, d end def explain_list(stage) # :nodoc: return unless DEBUG_RESOLVER data = yield $stderr.printf "%10s (%d entries)\n", stage.to_s.upcase, data.size unless data.empty? require 'pp' PP.pp data, $stderr end end ## # Creates an ActivationRequest for the given +dep+ and the last +possible+ # specification. # # Returns the Specification and the ActivationRequest def activation_request(dep, possible) # :nodoc: spec = possible.pop explain :activate, [spec.full_name, possible.size] explain :possible, possible activation_request = Gem::Resolver::ActivationRequest.new spec, dep, possible return spec, activation_request end def requests(s, act, reqs=[]) # :nodoc: return reqs if @ignore_dependencies s.fetch_development_dependencies if @development s.dependencies.reverse_each do |d| next if d.type == :development and not @development next if d.type == :development and @development_shallow and act.development? next if d.type == :development and @development_shallow and act.parent reqs << Gem::Resolver::DependencyRequest.new(d, act) @stats.requirement! end @set.prefetch reqs @stats.record_requirements reqs reqs end include Molinillo::UI def output @output ||= debug? ? $stdout : File.open(IO::NULL, 'w') end def debug? DEBUG_RESOLVER end include Molinillo::SpecificationProvider ## # Proceed with resolution! Returns an array of ActivationRequest objects. def resolve locking_dg = Molinillo::DependencyGraph.new Molinillo::Resolver.new(self, self).resolve(@needed.map {|d| DependencyRequest.new d, nil }, locking_dg).tsort.map(&:payload).compact rescue Molinillo::VersionConflict => e conflict = e.conflicts.values.first raise Gem::DependencyResolutionError, Conflict.new(conflict.requirement_trees.first.first, conflict.existing, conflict.requirement) ensure @output.close if defined?(@output) and !debug? end ## # Extracts the specifications that may be able to fulfill +dependency+ and # returns those that match the local platform and all those that match. def find_possible(dependency) # :nodoc: all = @set.find_all dependency if (skip_dep_gems = skip_gems[dependency.name]) && !skip_dep_gems.empty? matching = all.select do |api_spec| skip_dep_gems.any? {|s| api_spec.version == s.version } end all = matching unless matching.empty? end matching_platform = select_local_platforms all return matching_platform, all end ## # Returns the gems in +specs+ that match the local platform. def select_local_platforms(specs) # :nodoc: specs.select do |spec| Gem::Platform.installable? spec end end def search_for(dependency) possibles, all = find_possible(dependency) if !@soft_missing && possibles.empty? @missing << dependency exc = Gem::UnsatisfiableDependencyError.new dependency, all exc.errors = @set.errors raise exc end groups = Hash.new {|hash, key| hash[key] = [] } # create groups & sources in the same loop sources = possibles.map do |spec| source = spec.source groups[source] << spec source end.uniq.reverse activation_requests = [] sources.each do |source| groups[source]. sort_by {|spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }. map {|spec| ActivationRequest.new spec, dependency }. each {|activation_request| activation_requests << activation_request } end activation_requests end def dependencies_for(specification) return [] if @ignore_dependencies spec = specification.spec requests(spec, specification) end def requirement_satisfied_by?(requirement, activated, spec) matches_spec = requirement.matches_spec? spec return matches_spec if @soft_missing matches_spec && spec.spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && spec.spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version) end def name_for(dependency) dependency.name end def allow_missing?(dependency) @missing << dependency @soft_missing end def sort_dependencies(dependencies, activated, conflicts) dependencies.sort_by.with_index do |dependency, i| name = name_for(dependency) [ activated.vertex_named(name).payload ? 0 : 1, amount_constrained(dependency), conflicts[name] ? 0 : 1, activated.vertex_named(name).payload ? 0 : search_for(dependency).count, i, # for stable sort ] end end SINGLE_POSSIBILITY_CONSTRAINT_PENALTY = 1_000_000 private_constant :SINGLE_POSSIBILITY_CONSTRAINT_PENALTY if defined?(private_constant) # returns an integer \in (-\infty, 0] # a number closer to 0 means the dependency is less constraining # # dependencies w/ 0 or 1 possibilities (ignoring version requirements) # are given very negative values, so they _always_ sort first, # before dependencies that are unconstrained def amount_constrained(dependency) @amount_constrained ||= {} @amount_constrained[dependency.name] ||= begin name_dependency = Gem::Dependency.new(dependency.name) dependency_request_for_name = Gem::Resolver::DependencyRequest.new(name_dependency, dependency.requester) all = @set.find_all(dependency_request_for_name).size if all <= 1 all - SINGLE_POSSIBILITY_CONSTRAINT_PENALTY else search = search_for(dependency).size search - all end end end private :amount_constrained end require_relative 'resolver/activation_request' require_relative 'resolver/conflict' require_relative 'resolver/dependency_request' require_relative 'resolver/requirement_list' require_relative 'resolver/stats' require_relative 'resolver/set' require_relative 'resolver/api_set' require_relative 'resolver/composed_set' require_relative 'resolver/best_set' require_relative 'resolver/current_set' require_relative 'resolver/git_set' require_relative 'resolver/index_set' require_relative 'resolver/installer_set' require_relative 'resolver/lock_set' require_relative 'resolver/vendor_set' require_relative 'resolver/source_set' require_relative 'resolver/specification' require_relative 'resolver/spec_specification' require_relative 'resolver/api_specification' require_relative 'resolver/git_specification' require_relative 'resolver/index_specification' require_relative 'resolver/installed_specification' require_relative 'resolver/local_specification' require_relative 'resolver/lock_specification' require_relative 'resolver/vendor_specification' rubygems/rubygems/compatibility.rb 0000644 00000001776 15102661023 0013443 0 ustar 00 # frozen_string_literal: true # :stopdoc: #-- # This file contains all sorts of little compatibility hacks that we've # had to introduce over the years. Quarantining them into one file helps # us know when we can get rid of them. # # Ruby 1.9.x has introduced some things that are awkward, and we need to # support them, so we define some constants to use later. #++ # TODO remove at RubyGems 4 module Gem RubyGemsVersion = VERSION deprecate_constant(:RubyGemsVersion) RbConfigPriorities = %w[ MAJOR MINOR TEENY EXEEXT RUBY_SO_NAME arch bindir datadir libdir ruby_install_name ruby_version rubylibprefix sitedir sitelibdir vendordir vendorlibdir rubylibdir ].freeze unless defined?(ConfigMap) ## # Configuration settings from ::RbConfig ConfigMap = Hash.new do |cm, key| cm[key] = RbConfig::CONFIG[key.to_s] end deprecate_constant(:ConfigMap) else RbConfigPriorities.each do |key| ConfigMap[key.to_sym] = RbConfig::CONFIG[key] end end end rubygems/rubygems/request/http_pool.rb 0000644 00000002016 15102661023 0014256 0 ustar 00 # frozen_string_literal: true ## # A connection "pool" that only manages one connection for now. Provides # thread safe `checkout` and `checkin` methods. The pool consists of one # connection that corresponds to `http_args`. This class is private, do not # use it. class Gem::Request::HTTPPool # :nodoc: attr_reader :cert_files, :proxy_uri def initialize(http_args, cert_files, proxy_uri) @http_args = http_args @cert_files = cert_files @proxy_uri = proxy_uri @queue = Thread::SizedQueue.new 1 @queue << nil end def checkout @queue.pop || make_connection end def checkin(connection) @queue.push connection end def close_all until @queue.empty? if connection = @queue.pop(true) and connection.started? connection.finish end end @queue.push(nil) end private def make_connection setup_connection Gem::Request::ConnectionPools.client.new(*@http_args) end def setup_connection(connection) connection.start connection end end rubygems/rubygems/request/connection_pools.rb 0000644 00000004615 15102661023 0015630 0 ustar 00 # frozen_string_literal: true class Gem::Request::ConnectionPools # :nodoc: @client = Net::HTTP class << self attr_accessor :client end def initialize(proxy_uri, cert_files) @proxy_uri = proxy_uri @cert_files = cert_files @pools = {} @pool_mutex = Thread::Mutex.new end def pool_for(uri) http_args = net_http_args(uri, @proxy_uri) key = http_args + [https?(uri)] @pool_mutex.synchronize do @pools[key] ||= if https? uri Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri) else Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri) end end end def close_all @pools.each_value {|pool| pool.close_all } end private ## # Returns list of no_proxy entries (if any) from the environment def get_no_proxy_from_env env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY'] return [] if env_no_proxy.nil? or env_no_proxy.empty? env_no_proxy.split(/\s*,\s*/) end def https?(uri) uri.scheme.downcase == 'https' end def no_proxy?(host, env_no_proxy) host = host.downcase env_no_proxy.any? do |pattern| env_no_proxy_pattern = pattern.downcase.dup # Remove dot in front of pattern for wildcard matching env_no_proxy_pattern[0] = "" if env_no_proxy_pattern[0] == "." host_tokens = host.split(".") pattern_tokens = env_no_proxy_pattern.split(".") intersection = (host_tokens - pattern_tokens) | (pattern_tokens - host_tokens) # When we do the split into tokens we miss a dot character, so add it back if we need it missing_dot = intersection.length > 0 ? 1 : 0 start = intersection.join(".").size + missing_dot no_proxy_host = host[start..-1] env_no_proxy_pattern == no_proxy_host end end def net_http_args(uri, proxy_uri) hostname = uri.hostname net_http_args = [hostname, uri.port] no_proxy = get_no_proxy_from_env if proxy_uri and not no_proxy?(hostname, no_proxy) proxy_hostname = proxy_uri.respond_to?(:hostname) ? proxy_uri.hostname : proxy_uri.host net_http_args + [ proxy_hostname, proxy_uri.port, Gem::UriFormatter.new(proxy_uri.user).unescape, Gem::UriFormatter.new(proxy_uri.password).unescape, ] elsif no_proxy? hostname, no_proxy net_http_args + [nil, nil] else net_http_args end end end rubygems/rubygems/request/https_pool.rb 0000644 00000000352 15102661023 0014442 0 ustar 00 # frozen_string_literal: true class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc: private def setup_connection(connection) Gem::Request.configure_connection_for_https(connection, @cert_files) super end end rubygems/rubygems/source/lock.rb 0000644 00000001636 15102661023 0013015 0 ustar 00 # frozen_string_literal: true ## # A Lock source wraps an installed gem's source and sorts before other sources # during dependency resolution. This allows RubyGems to prefer gems from # dependency lock files. class Gem::Source::Lock < Gem::Source ## # The wrapped Gem::Source attr_reader :wrapped ## # Creates a new Lock source that wraps +source+ and moves it earlier in the # sort list. def initialize(source) @wrapped = source end def <=>(other) # :nodoc: case other when Gem::Source::Lock then @wrapped <=> other.wrapped when Gem::Source then 1 else nil end end def ==(other) # :nodoc: 0 == (self <=> other) end def hash # :nodoc: @wrapped.hash ^ 3 end ## # Delegates to the wrapped source's fetch_spec method. def fetch_spec(name_tuple) @wrapped.fetch_spec name_tuple end def uri # :nodoc: @wrapped.uri end end rubygems/rubygems/source/git.rb 0000644 00000012372 15102661023 0012647 0 ustar 00 # frozen_string_literal: true ## # A git gem for use in a gem dependencies file. # # Example: # # source = # Gem::Source::Git.new 'rake', 'git@example:rake.git', 'rake-10.1.0', false # # source.specs class Gem::Source::Git < Gem::Source ## # The name of the gem created by this git gem. attr_reader :name ## # The commit reference used for checking out this git gem. attr_reader :reference ## # When false the cache for this repository will not be updated. attr_accessor :remote ## # The git repository this gem is sourced from. attr_reader :repository ## # The directory for cache and git gem installation attr_accessor :root_dir ## # Does this repository need submodules checked out too? attr_reader :need_submodules ## # Creates a new git gem source for a gems from loaded from +repository+ at # the given +reference+. The +name+ is only used to track the repository # back to a gem dependencies file, it has no real significance as a git # repository may contain multiple gems. If +submodules+ is true, submodules # will be checked out when the gem is installed. def initialize(name, repository, reference, submodules = false) super repository @name = name @repository = repository @reference = reference @need_submodules = submodules @remote = true @root_dir = Gem.dir @git = ENV['git'] || 'git' end def <=>(other) case other when Gem::Source::Git then 0 when Gem::Source::Vendor, Gem::Source::Lock then -1 when Gem::Source then 1 else nil end end def ==(other) # :nodoc: super and @name == other.name and @repository == other.repository and @reference == other.reference and @need_submodules == other.need_submodules end ## # Checks out the files for the repository into the install_dir. def checkout # :nodoc: cache return false unless File.exist? repo_cache_dir unless File.exist? install_dir system @git, 'clone', '--quiet', '--no-checkout', repo_cache_dir, install_dir end Dir.chdir install_dir do system @git, 'fetch', '--quiet', '--force', '--tags', install_dir success = system @git, 'reset', '--quiet', '--hard', rev_parse if @need_submodules _, status = Open3.capture2e(@git, 'submodule', 'update', '--quiet', '--init', '--recursive') success &&= status.success? end success end end ## # Creates a local cache repository for the git gem. def cache # :nodoc: return unless @remote if File.exist? repo_cache_dir Dir.chdir repo_cache_dir do system @git, 'fetch', '--quiet', '--force', '--tags', @repository, 'refs/heads/*:refs/heads/*' end else system @git, 'clone', '--quiet', '--bare', '--no-hardlinks', @repository, repo_cache_dir end end ## # Directory where git gems get unpacked and so-forth. def base_dir # :nodoc: File.join @root_dir, 'bundler' end ## # A short reference for use in git gem directories def dir_shortref # :nodoc: rev_parse[0..11] end ## # Nothing to download for git gems def download(full_spec, path) # :nodoc: end ## # The directory where the git gem will be installed. def install_dir # :nodoc: return unless File.exist? repo_cache_dir File.join base_dir, 'gems', "#{@name}-#{dir_shortref}" end def pretty_print(q) # :nodoc: q.group 2, '[Git: ', ']' do q.breakable q.text @repository q.breakable q.text @reference end end ## # The directory where the git gem's repository will be cached. def repo_cache_dir # :nodoc: File.join @root_dir, 'cache', 'bundler', 'git', "#{@name}-#{uri_hash}" end ## # Converts the git reference for the repository into a commit hash. def rev_parse # :nodoc: hash = nil Dir.chdir repo_cache_dir do hash = Gem::Util.popen(@git, 'rev-parse', @reference).strip end raise Gem::Exception, "unable to find reference #{@reference} in #{@repository}" unless $?.success? hash end ## # Loads all gemspecs in the repository def specs checkout return [] unless install_dir Dir.chdir install_dir do Dir['{,*,*/*}.gemspec'].map do |spec_file| directory = File.dirname spec_file file = File.basename spec_file Dir.chdir directory do spec = Gem::Specification.load file if spec spec.base_dir = base_dir spec.extension_dir = File.join base_dir, 'extensions', Gem::Platform.local.to_s, Gem.extension_api_version, "#{name}-#{dir_shortref}" spec.full_gem_path = File.dirname spec.loaded_from if spec end spec end end.compact end end ## # A hash for the git gem based on the git repository URI. def uri_hash # :nodoc: require_relative '../openssl' normalized = if @repository =~ %r{^\w+://(\w+@)?} uri = URI(@repository).normalize.to_s.sub %r{/$},'' uri.sub(/\A(\w+)/) { $1.downcase } else @repository end OpenSSL::Digest::SHA1.hexdigest normalized end end rubygems/rubygems/source/local.rb 0000644 00000005457 15102661023 0013164 0 ustar 00 # frozen_string_literal: true ## # The local source finds gems in the current directory for fulfilling # dependencies. class Gem::Source::Local < Gem::Source def initialize # :nodoc: @specs = nil @api_uri = nil @uri = nil @load_specs_names = {} end ## # Local sorts before Gem::Source and after Gem::Source::Installed def <=>(other) case other when Gem::Source::Installed, Gem::Source::Lock then -1 when Gem::Source::Local then 0 when Gem::Source then 1 else nil end end def inspect # :nodoc: keys = @specs ? @specs.keys.sort : 'NOT LOADED' "#<%s specs: %p>" % [self.class, keys] end def load_specs(type) # :nodoc: @load_specs_names[type] ||= begin names = [] @specs = {} Dir["*.gem"].each do |file| begin pkg = Gem::Package.new(file) rescue SystemCallError, Gem::Package::FormatError # ignore else tup = pkg.spec.name_tuple @specs[tup] = [File.expand_path(file), pkg] case type when :released unless pkg.spec.version.prerelease? names << pkg.spec.name_tuple end when :prerelease if pkg.spec.version.prerelease? names << pkg.spec.name_tuple end when :latest tup = pkg.spec.name_tuple cur = names.find {|x| x.name == tup.name } if !cur names << tup elsif cur.version < tup.version names.delete cur names << tup end else names << pkg.spec.name_tuple end end end names end end def find_gem(gem_name, version = Gem::Requirement.default, # :nodoc: prerelease = false) load_specs :complete found = [] @specs.each do |n, data| if n.name == gem_name s = data[1].spec if version.satisfied_by?(s.version) if prerelease found << s elsif !s.version.prerelease? || version.prerelease? found << s end end end end found.max_by {|s| s.version } end def fetch_spec(name) # :nodoc: load_specs :complete if data = @specs[name] data.last.spec else raise Gem::Exception, "Unable to find spec for #{name.inspect}" end end def download(spec, cache_dir = nil) # :nodoc: load_specs :complete @specs.each do |name, data| return data[0] if data[1].spec == spec end raise Gem::Exception, "Unable to find file for '#{spec.full_name}'" end def pretty_print(q) # :nodoc: q.group 2, '[Local gems:', ']' do q.breakable q.seplist @specs.keys do |v| q.text v.full_name end end end end rubygems/rubygems/source/vendor.rb 0000644 00000000723 15102661023 0013356 0 ustar 00 # frozen_string_literal: true ## # This represents a vendored source that is similar to an installed gem. class Gem::Source::Vendor < Gem::Source::Installed ## # Creates a new Vendor source for a gem that was unpacked at +path+. def initialize(path) @uri = path end def <=>(other) case other when Gem::Source::Lock then -1 when Gem::Source::Vendor then 0 when Gem::Source then 1 else nil end end end rubygems/rubygems/source/installed.rb 0000644 00000001225 15102661023 0014036 0 ustar 00 # frozen_string_literal: true ## # Represents an installed gem. This is used for dependency resolution. class Gem::Source::Installed < Gem::Source def initialize # :nodoc: @uri = nil end ## # Installed sources sort before all other sources def <=>(other) case other when Gem::Source::Git, Gem::Source::Lock, Gem::Source::Vendor then -1 when Gem::Source::Installed then 0 when Gem::Source then 1 else nil end end ## # We don't need to download an installed gem def download(spec, path) nil end def pretty_print(q) # :nodoc: q.text '[Installed]' end end rubygems/rubygems/source/specific_file.rb 0000644 00000002750 15102661023 0014647 0 ustar 00 # frozen_string_literal: true ## # A source representing a single .gem file. This is used for installation of # local gems. class Gem::Source::SpecificFile < Gem::Source ## # The path to the gem for this specific file. attr_reader :path ## # Creates a new SpecificFile for the gem in +file+ def initialize(file) @uri = nil @path = ::File.expand_path(file) @package = Gem::Package.new @path @spec = @package.spec @name = @spec.name_tuple end ## # The Gem::Specification extracted from this .gem. attr_reader :spec def load_specs(*a) # :nodoc: [@name] end def fetch_spec(name) # :nodoc: return @spec if name == @name raise Gem::Exception, "Unable to find '#{name}'" @spec end def download(spec, dir = nil) # :nodoc: return @path if spec == @spec raise Gem::Exception, "Unable to download '#{spec.full_name}'" end def pretty_print(q) # :nodoc: q.group 2, '[SpecificFile:', ']' do q.breakable q.text @path end end ## # Orders this source against +other+. # # If +other+ is a SpecificFile from a different gem name +nil+ is returned. # # If +other+ is a SpecificFile from the same gem name the versions are # compared using Gem::Version#<=> # # Otherwise Gem::Source#<=> is used. def <=>(other) case other when Gem::Source::SpecificFile then return nil if @spec.name != other.spec.name @spec.version <=> other.spec.version else super end end end rubygems/rubygems/optparse/COPYING 0000644 00000004573 15102661023 0013133 0 ustar 00 Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>. You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a. place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b. use the modified software only within your corporation or organization. c. give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d. make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a. distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b. accompany the distribution with the machine-readable source of the software. c. give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d. make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. rubygems/rubygems/optparse/lib/optionparser.rb 0000644 00000000073 15102661023 0015707 0 ustar 00 # frozen_string_literal: false require_relative 'optparse' rubygems/rubygems/optparse/lib/optparse/kwargs.rb 0000644 00000001061 15102661023 0016313 0 ustar 00 # frozen_string_literal: true require 'rubygems/optparse/lib/optparse' class Gem::OptionParser # :call-seq: # define_by_keywords(options, method, **params) # # :include: ../../doc/optparse/creates_option.rdoc # def define_by_keywords(options, meth, **opts) meth.parameters.each do |type, name| case type when :key, :keyreq op, cl = *(type == :key ? %w"[ ]" : ["", ""]) define("--#{name}=#{op}#{name.upcase}#{cl}", *opts[name]) do |o| options[name] = o end end end options end end rubygems/rubygems/optparse/lib/optparse/ac.rb 0000644 00000003056 15102661023 0015406 0 ustar 00 # frozen_string_literal: false require 'rubygems/optparse/lib/optparse' class Gem::OptionParser::AC < Gem::OptionParser private def _check_ac_args(name, block) unless /\A\w[-\w]*\z/ =~ name raise ArgumentError, name end unless block raise ArgumentError, "no block given", ParseError.filter_backtrace(caller) end end ARG_CONV = proc {|val| val.nil? ? true : val} def _ac_arg_enable(prefix, name, help_string, block) _check_ac_args(name, block) sdesc = [] ldesc = ["--#{prefix}-#{name}"] desc = [help_string] q = name.downcase ac_block = proc {|val| block.call(ARG_CONV.call(val))} enable = Switch::PlacedArgument.new(nil, ARG_CONV, sdesc, ldesc, nil, desc, ac_block) disable = Switch::NoArgument.new(nil, proc {false}, sdesc, ldesc, nil, desc, ac_block) top.append(enable, [], ["enable-" + q], disable, ['disable-' + q]) enable end public def ac_arg_enable(name, help_string, &block) _ac_arg_enable("enable", name, help_string, block) end def ac_arg_disable(name, help_string, &block) _ac_arg_enable("disable", name, help_string, block) end def ac_arg_with(name, help_string, &block) _check_ac_args(name, block) sdesc = [] ldesc = ["--with-#{name}"] desc = [help_string] q = name.downcase with = Switch::PlacedArgument.new(*search(:atype, String), sdesc, ldesc, nil, desc, block) without = Switch::NoArgument.new(nil, proc {}, sdesc, ldesc, nil, desc, block) top.append(with, [], ["with-" + q], without, ['without-' + q]) with end end rubygems/rubygems/optparse/lib/optparse/date.rb 0000644 00000000616 15102661023 0015737 0 ustar 00 # frozen_string_literal: false require 'rubygems/optparse/lib/optparse' require 'date' Gem::OptionParser.accept(DateTime) do |s,| begin DateTime.parse(s) if s rescue ArgumentError raise Gem::OptionParser::InvalidArgument, s end end Gem::OptionParser.accept(Date) do |s,| begin Date.parse(s) if s rescue ArgumentError raise Gem::OptionParser::InvalidArgument, s end end rubygems/rubygems/optparse/lib/optparse/shellwords.rb 0000644 00000000263 15102661023 0017206 0 ustar 00 # frozen_string_literal: false # -*- ruby -*- require 'shellwords' require 'rubygems/optparse/lib/optparse' Gem::OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)} rubygems/rubygems/optparse/lib/optparse/time.rb 0000644 00000000373 15102661023 0015760 0 ustar 00 # frozen_string_literal: false require 'rubygems/optparse/lib/optparse' require 'time' Gem::OptionParser.accept(Time) do |s,| begin (Time.httpdate(s) rescue Time.parse(s)) if s rescue raise Gem::OptionParser::InvalidArgument, s end end rubygems/rubygems/optparse/lib/optparse/version.rb 0000644 00000004027 15102661023 0016507 0 ustar 00 # frozen_string_literal: false # Gem::OptionParser internal utility class << Gem::OptionParser def show_version(*pkgs) progname = ARGV.options.program_name result = false show = proc do |klass, cname, version| str = "#{progname}" unless klass == ::Object and cname == :VERSION version = version.join(".") if Array === version str << ": #{klass}" unless klass == Object str << " version #{version}" end [:Release, :RELEASE].find do |rel| if klass.const_defined?(rel) str << " (#{klass.const_get(rel)})" end end puts str result = true end if pkgs.size == 1 and pkgs[0] == "all" self.search_const(::Object, /\AV(?:ERSION|ersion)\z/) do |klass, cname, version| unless cname[1] == ?e and klass.const_defined?(:Version) show.call(klass, cname.intern, version) end end else pkgs.each do |pkg| begin pkg = pkg.split(/::|\//).inject(::Object) {|m, c| m.const_get(c)} v = case when pkg.const_defined?(:Version) pkg.const_get(n = :Version) when pkg.const_defined?(:VERSION) pkg.const_get(n = :VERSION) else n = nil "unknown" end show.call(pkg, n, v) rescue NameError end end end result end def each_const(path, base = ::Object) path.split(/::|\//).inject(base) do |klass, name| raise NameError, path unless Module === klass klass.constants.grep(/#{name}/i) do |c| klass.const_defined?(c) or next klass.const_get(c) end end end def search_const(klass, name) klasses = [klass] while klass = klasses.shift klass.constants.each do |cname| klass.const_defined?(cname) or next const = klass.const_get(cname) yield klass, cname, const if name === cname klasses << const if Module === const and const != ::Object end end end end rubygems/rubygems/optparse/lib/optparse/uri.rb 0000644 00000000236 15102661023 0015617 0 ustar 00 # frozen_string_literal: false # -*- ruby -*- require 'rubygems/optparse/lib/optparse' require 'uri' Gem::OptionParser.accept(URI) {|s,| URI.parse(s) if s} rubygems/rubygems/optparse/lib/optparse.rb 0000644 00000166727 15102661023 0015042 0 ustar 00 # frozen_string_literal: true # # optparse.rb - command-line option analysis with the Gem::OptionParser class. # # Author:: Nobu Nakada # Documentation:: Nobu Nakada and Gavin Sinclair. # # See Gem::OptionParser for documentation. # #-- # == Developer Documentation (not for RDoc output) # # === Class tree # # - Gem::OptionParser:: front end # - Gem::OptionParser::Switch:: each switches # - Gem::OptionParser::List:: options list # - Gem::OptionParser::ParseError:: errors on parsing # - Gem::OptionParser::AmbiguousOption # - Gem::OptionParser::NeedlessArgument # - Gem::OptionParser::MissingArgument # - Gem::OptionParser::InvalidOption # - Gem::OptionParser::InvalidArgument # - Gem::OptionParser::AmbiguousArgument # # === Object relationship diagram # # +--------------+ # | Gem::OptionParser |<>-----+ # +--------------+ | +--------+ # | ,-| Switch | # on_head -------->+---------------+ / +--------+ # accept/reject -->| List |<|>- # | |<|>- +----------+ # on ------------->+---------------+ `-| argument | # : : | class | # +---------------+ |==========| # on_tail -------->| | |pattern | # +---------------+ |----------| # Gem::OptionParser.accept ->| DefaultList | |converter | # reject |(shared between| +----------+ # | all instances)| # +---------------+ # #++ # # == Gem::OptionParser # # === New to \Gem::OptionParser? # # See the {Tutorial}[./doc/optparse/tutorial_rdoc.html]. # # === Introduction # # Gem::OptionParser is a class for command-line option analysis. It is much more # advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented # solution. # # === Features # # 1. The argument specification and the code to handle it are written in the # same place. # 2. It can output an option summary; you don't need to maintain this string # separately. # 3. Optional and mandatory arguments are specified very gracefully. # 4. Arguments can be automatically converted to a specified class. # 5. Arguments can be restricted to a certain set. # # All of these features are demonstrated in the examples below. See # #make_switch for full documentation. # # === Minimal example # # require 'rubygems/optparse/lib/optparse' # # options = {} # Gem::OptionParser.new do |parser| # parser.banner = "Usage: example.rb [options]" # # parser.on("-v", "--[no-]verbose", "Run verbosely") do |v| # options[:verbose] = v # end # end.parse! # # p options # p ARGV # # === Generating Help # # Gem::OptionParser can be used to automatically generate help for the commands you # write: # # require 'rubygems/optparse/lib/optparse' # # Options = Struct.new(:name) # # class Parser # def self.parse(options) # args = Options.new("world") # # opt_parser = Gem::OptionParser.new do |parser| # parser.banner = "Usage: example.rb [options]" # # parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n| # args.name = n # end # # parser.on("-h", "--help", "Prints this help") do # puts parser # exit # end # end # # opt_parser.parse!(options) # return args # end # end # options = Parser.parse %w[--help] # # #=> # # Usage: example.rb [options] # # -n, --name=NAME Name to say hello to # # -h, --help Prints this help # # === Required Arguments # # For options that require an argument, option specification strings may include an # option name in all caps. If an option is used without the required argument, # an exception will be raised. # # require 'rubygems/optparse/lib/optparse' # # options = {} # Gem::OptionParser.new do |parser| # parser.on("-r", "--require LIBRARY", # "Require the LIBRARY before executing your script") do |lib| # puts "You required #{lib}!" # end # end.parse! # # Used: # # $ ruby optparse-test.rb -r # optparse-test.rb:9:in `<main>': missing argument: -r (Gem::OptionParser::MissingArgument) # $ ruby optparse-test.rb -r my-library # You required my-library! # # === Type Coercion # # Gem::OptionParser supports the ability to coerce command line arguments # into objects for us. # # Gem::OptionParser comes with a few ready-to-use kinds of type # coercion. They are: # # - Date -- Anything accepted by +Date.parse+ # - DateTime -- Anything accepted by +DateTime.parse+ # - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+ # - URI -- Anything accepted by +URI.parse+ # - Shellwords -- Anything accepted by +Shellwords.shellwords+ # - String -- Any non-empty string # - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040) # - Float -- Any float. (e.g. 10, 3.14, -100E+13) # - Numeric -- Any integer, float, or rational (1, 3.4, 1/3) # - DecimalInteger -- Like +Integer+, but no octal format. # - OctalInteger -- Like +Integer+, but no decimal format. # - DecimalNumeric -- Decimal integer or float. # - TrueClass -- Accepts '+, yes, true, -, no, false' and # defaults as +true+ # - FalseClass -- Same as +TrueClass+, but defaults to +false+ # - Array -- Strings separated by ',' (e.g. 1,2,3) # - Regexp -- Regular expressions. Also includes options. # # We can also add our own coercions, which we will cover below. # # ==== Using Built-in Conversions # # As an example, the built-in +Time+ conversion is used. The other built-in # conversions behave in the same way. # Gem::OptionParser will attempt to parse the argument # as a +Time+. If it succeeds, that time will be passed to the # handler block. Otherwise, an exception will be raised. # # require 'rubygems/optparse/lib/optparse' # require 'rubygems/optparse/lib/optparse/time' # Gem::OptionParser.new do |parser| # parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time| # p time # end # end.parse! # # Used: # # $ ruby optparse-test.rb -t nonsense # ... invalid argument: -t nonsense (Gem::OptionParser::InvalidArgument) # $ ruby optparse-test.rb -t 10-11-12 # 2010-11-12 00:00:00 -0500 # $ ruby optparse-test.rb -t 9:30 # 2014-08-13 09:30:00 -0400 # # ==== Creating Custom Conversions # # The +accept+ method on Gem::OptionParser may be used to create converters. # It specifies which conversion block to call whenever a class is specified. # The example below uses it to fetch a +User+ object before the +on+ handler receives it. # # require 'rubygems/optparse/lib/optparse' # # User = Struct.new(:id, :name) # # def find_user id # not_found = ->{ raise "No User Found for id #{id}" } # [ User.new(1, "Sam"), # User.new(2, "Gandalf") ].find(not_found) do |u| # u.id == id # end # end # # op = Gem::OptionParser.new # op.accept(User) do |user_id| # find_user user_id.to_i # end # # op.on("--user ID", User) do |user| # puts user # end # # op.parse! # # Used: # # $ ruby optparse-test.rb --user 1 # #<struct User id=1, name="Sam"> # $ ruby optparse-test.rb --user 2 # #<struct User id=2, name="Gandalf"> # $ ruby optparse-test.rb --user 3 # optparse-test.rb:15:in `block in find_user': No User Found for id 3 (RuntimeError) # # === Store options to a Hash # # The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash. # # require 'rubygems/optparse/lib/optparse' # # options = {} # Gem::OptionParser.new do |parser| # parser.on('-a') # parser.on('-b NUM', Integer) # parser.on('-v', '--verbose') # end.parse!(into: options) # # p options # # Used: # # $ ruby optparse-test.rb -a # {:a=>true} # $ ruby optparse-test.rb -a -v # {:a=>true, :verbose=>true} # $ ruby optparse-test.rb -a -b 100 # {:a=>true, :b=>100} # # === Complete example # # The following example is a complete Ruby program. You can run it and see the # effect of specifying various options. This is probably the best way to learn # the features of +optparse+. # # require 'rubygems/optparse/lib/optparse' # require 'rubygems/optparse/lib/optparse/time' # require 'ostruct' # require 'pp' # # class OptparseExample # Version = '1.0.0' # # CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary] # CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" } # # class ScriptOptions # attr_accessor :library, :inplace, :encoding, :transfer_type, # :verbose, :extension, :delay, :time, :record_separator, # :list # # def initialize # self.library = [] # self.inplace = false # self.encoding = "utf8" # self.transfer_type = :auto # self.verbose = false # end # # def define_options(parser) # parser.banner = "Usage: example.rb [options]" # parser.separator "" # parser.separator "Specific options:" # # # add additional options # perform_inplace_option(parser) # delay_execution_option(parser) # execute_at_time_option(parser) # specify_record_separator_option(parser) # list_example_option(parser) # specify_encoding_option(parser) # optional_option_argument_with_keyword_completion_option(parser) # boolean_verbose_option(parser) # # parser.separator "" # parser.separator "Common options:" # # No argument, shows at tail. This will print an options summary. # # Try it and see! # parser.on_tail("-h", "--help", "Show this message") do # puts parser # exit # end # # Another typical switch to print the version. # parser.on_tail("--version", "Show version") do # puts Version # exit # end # end # # def perform_inplace_option(parser) # # Specifies an optional option argument # parser.on("-i", "--inplace [EXTENSION]", # "Edit ARGV files in place", # "(make backup if EXTENSION supplied)") do |ext| # self.inplace = true # self.extension = ext || '' # self.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot. # end # end # # def delay_execution_option(parser) # # Cast 'delay' argument to a Float. # parser.on("--delay N", Float, "Delay N seconds before executing") do |n| # self.delay = n # end # end # # def execute_at_time_option(parser) # # Cast 'time' argument to a Time object. # parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time| # self.time = time # end # end # # def specify_record_separator_option(parser) # # Cast to octal integer. # parser.on("-F", "--irs [OCTAL]", Gem::OptionParser::OctalInteger, # "Specify record separator (default \\0)") do |rs| # self.record_separator = rs # end # end # # def list_example_option(parser) # # List of arguments. # parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list| # self.list = list # end # end # # def specify_encoding_option(parser) # # Keyword completion. We are specifying a specific set of arguments (CODES # # and CODE_ALIASES - notice the latter is a Hash), and the user may provide # # the shortest unambiguous text. # code_list = (CODE_ALIASES.keys + CODES).join(', ') # parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding", # "(#{code_list})") do |encoding| # self.encoding = encoding # end # end # # def optional_option_argument_with_keyword_completion_option(parser) # # Optional '--type' option argument with keyword completion. # parser.on("--type [TYPE]", [:text, :binary, :auto], # "Select transfer type (text, binary, auto)") do |t| # self.transfer_type = t # end # end # # def boolean_verbose_option(parser) # # Boolean switch. # parser.on("-v", "--[no-]verbose", "Run verbosely") do |v| # self.verbose = v # end # end # end # # # # # Return a structure describing the options. # # # def parse(args) # # The options specified on the command line will be collected in # # *options*. # # @options = ScriptOptions.new # @args = Gem::OptionParser.new do |parser| # @options.define_options(parser) # parser.parse!(args) # end # @options # end # # attr_reader :parser, :options # end # class OptparseExample # # example = OptparseExample.new # options = example.parse(ARGV) # pp options # example.options # pp ARGV # # === Shell Completion # # For modern shells (e.g. bash, zsh, etc.), you can use shell # completion for command line options. # # === Further documentation # # The above examples, along with the accompanying # {Tutorial}[./doc/optparse/tutorial_rdoc.html], # should be enough to learn how to use this class. # If you have any questions, file a ticket at http://bugs.ruby-lang.org. # class Gem::OptionParser Gem::OptionParser::Version = "0.2.0" # :stopdoc: NoArgument = [NO_ARGUMENT = :NONE, nil].freeze RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze # :startdoc: # # Keyword completion module. This allows partial arguments to be specified # and resolved against a list of acceptable values. # module Completion def self.regexp(key, icase) Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase) end def self.candidate(key, icase = false, pat = nil, &block) pat ||= Completion.regexp(key, icase) candidates = [] block.call do |k, *v| (if Regexp === k kn = "" k === key else kn = defined?(k.id2name) ? k.id2name : k pat === kn end) or next v << k if v.empty? candidates << [k, v, kn] end candidates end def candidate(key, icase = false, pat = nil) Completion.candidate(key, icase, pat, &method(:each)) end public def complete(key, icase = false, pat = nil) candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size} if candidates.size == 1 canon, sw, * = candidates[0] elsif candidates.size > 1 canon, sw, cn = candidates.shift candidates.each do |k, v, kn| next if sw == v if String === cn and String === kn if cn.rindex(kn, 0) canon, sw, cn = k, v, kn next elsif kn.rindex(cn, 0) next end end throw :ambiguous, key end end if canon block_given? or return key, *sw yield(key, *sw) end end def convert(opt = nil, val = nil, *) val end end # # Map from option/keyword string to object with completion. # class OptionMap < Hash include Completion end # # Individual switch class. Not important to the user. # # Defined within Switch are several Switch-derived classes: NoArgument, # RequiredArgument, etc. # class Switch attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block # # Guesses argument style from +arg+. Returns corresponding # Gem::OptionParser::Switch class (OptionalArgument, etc.). # def self.guess(arg) case arg when "" t = self when /\A=?\[/ t = Switch::OptionalArgument when /\A\s+\[/ t = Switch::PlacedArgument else t = Switch::RequiredArgument end self >= t or incompatible_argument_styles(arg, t) t end def self.incompatible_argument_styles(arg, t) raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}", ParseError.filter_backtrace(caller(2))) end def self.pattern NilClass end def initialize(pattern = nil, conv = nil, short = nil, long = nil, arg = nil, desc = ([] if short or long), block = nil, &_block) raise if Array === pattern block ||= _block @pattern, @conv, @short, @long, @arg, @desc, @block = pattern, conv, short, long, arg, desc, block end # # Parses +arg+ and returns rest of +arg+ and matched portion to the # argument pattern. Yields when the pattern doesn't match substring. # def parse_arg(arg) # :nodoc: pattern or return nil, [arg] unless m = pattern.match(arg) yield(InvalidArgument, arg) return arg, [] end if String === m m = [s = m] else m = m.to_a s = m[0] return nil, m unless String === s end raise InvalidArgument, arg unless arg.rindex(s, 0) return nil, m if s.length == arg.length yield(InvalidArgument, arg) # didn't match whole arg return arg[s.length..-1], m end private :parse_arg # # Parses argument, converts and returns +arg+, +block+ and result of # conversion. Yields at semi-error condition instead of raising an # exception. # def conv_arg(arg, val = []) # :nodoc: if conv val = conv.call(*val) else val = proc {|v| v}.call(*val) end return arg, block, val end private :conv_arg # # Produces the summary text. Each line of the summary is yielded to the # block (without newline). # # +sdone+:: Already summarized short style options keyed hash. # +ldone+:: Already summarized long style options keyed hash. # +width+:: Width of left side (option part). In other words, the right # side (description part) starts after +width+ columns. # +max+:: Maximum width of left side -> the options are filled within # +max+ columns. # +indent+:: Prefix string indents all summarized lines. # def summarize(sdone = {}, ldone = {}, width = 1, max = width - 1, indent = "") sopts, lopts = [], [], nil @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long return if sopts.empty? and lopts.empty? # completely hidden left = [sopts.join(', ')] right = desc.dup while s = lopts.shift l = left[-1].length + s.length l += arg.length if left.size == 1 && arg l < max or sopts.empty? or left << +'' left[-1] << (left[-1].empty? ? ' ' * 4 : ', ') << s end if arg left[0] << (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg) end mlen = left.collect {|ss| ss.length}.max.to_i while mlen > width and l = left.shift mlen = left.collect {|ss| ss.length}.max.to_i if l.length == mlen if l.length < width and (r = right[0]) and !r.empty? l = l.to_s.ljust(width) + ' ' + r right.shift end yield(indent + l) end while begin l = left.shift; r = right.shift; l or r end l = l.to_s.ljust(width) + ' ' + r if r and !r.empty? yield(indent + l) end self end def add_banner(to) # :nodoc: unless @short or @long s = desc.join to << " [" + s + "]..." unless s.empty? end to end def match_nonswitch?(str) # :nodoc: @pattern =~ str unless @short or @long end # # Main name of the switch. # def switch_name (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '') end def compsys(sdone, ldone) # :nodoc: sopts, lopts = [], [] @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long return if sopts.empty? and lopts.empty? # completely hidden (sopts+lopts).each do |opt| # "(-x -c -r)-l[left justify]" if /^--\[no-\](.+)$/ =~ opt o = $1 yield("--#{o}", desc.join("")) yield("--no-#{o}", desc.join("")) else yield("#{opt}", desc.join("")) end end end # # Switch that takes no arguments. # class NoArgument < self # # Raises an exception if any arguments given. # def parse(arg, argv) yield(NeedlessArgument, arg) if arg conv_arg(arg) end def self.incompatible_argument_styles(*) end def self.pattern Object end end # # Switch that takes an argument. # class RequiredArgument < self # # Raises an exception if argument is not present. # def parse(arg, argv) unless arg raise MissingArgument if argv.empty? arg = argv.shift end conv_arg(*parse_arg(arg, &method(:raise))) end end # # Switch that can omit argument. # class OptionalArgument < self # # Parses argument if given, or uses default value. # def parse(arg, argv, &error) if arg conv_arg(*parse_arg(arg, &error)) else conv_arg(arg) end end end # # Switch that takes an argument, which does not begin with '-'. # class PlacedArgument < self # # Returns nil if argument is not present or begins with '-'. # def parse(arg, argv, &error) if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0])) return nil, block, nil end opt = (val = parse_arg(val, &error))[1] val = conv_arg(*val) if opt and !arg argv.shift else val[0] = nil end val end end end # # Simple option list providing mapping from short and/or long option # string to Gem::OptionParser::Switch and mapping from acceptable argument to # matching pattern and converter pair. Also provides summary feature. # class List # Map from acceptable argument types to pattern and converter pairs. attr_reader :atype # Map from short style option switches to actual switch objects. attr_reader :short # Map from long style option switches to actual switch objects. attr_reader :long # List of all switches and summary string. attr_reader :list # # Just initializes all instance variables. # def initialize @atype = {} @short = OptionMap.new @long = OptionMap.new @list = [] end # # See Gem::OptionParser.accept. # def accept(t, pat = /.*/m, &block) if pat pat.respond_to?(:match) or raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2)) else pat = t if t.respond_to?(:match) end unless block block = pat.method(:convert).to_proc if pat.respond_to?(:convert) end @atype[t] = [pat, block] end # # See Gem::OptionParser.reject. # def reject(t) @atype.delete(t) end # # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+. # # +sw+:: Gem::OptionParser::Switch instance to be added. # +sopts+:: Short style option list. # +lopts+:: Long style option list. # +nlopts+:: Negated long style options list. # def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc: sopts.each {|o| @short[o] = sw} if sopts lopts.each {|o| @long[o] = sw} if lopts nlopts.each {|o| @long[o] = nsw} if nsw and nlopts used = @short.invert.update(@long.invert) @list.delete_if {|o| Switch === o and !used[o]} end private :update # # Inserts +switch+ at the head of the list, and associates short, long # and negated long options. Arguments are: # # +switch+:: Gem::OptionParser::Switch instance to be inserted. # +short_opts+:: List of short style options. # +long_opts+:: List of long style options. # +nolong_opts+:: List of long style options with "no-" prefix. # # prepend(switch, short_opts, long_opts, nolong_opts) # def prepend(*args) update(*args) @list.unshift(args[0]) end # # Appends +switch+ at the tail of the list, and associates short, long # and negated long options. Arguments are: # # +switch+:: Gem::OptionParser::Switch instance to be inserted. # +short_opts+:: List of short style options. # +long_opts+:: List of long style options. # +nolong_opts+:: List of long style options with "no-" prefix. # # append(switch, short_opts, long_opts, nolong_opts) # def append(*args) update(*args) @list.push(args[0]) end # # Searches +key+ in +id+ list. The result is returned or yielded if a # block is given. If it isn't found, nil is returned. # def search(id, key) if list = __send__(id) val = list.fetch(key) {return nil} block_given? ? yield(val) : val end end # # Searches list +id+ for +opt+ and the optional patterns for completion # +pat+. If +icase+ is true, the search is case insensitive. The result # is returned or yielded if a block is given. If it isn't found, nil is # returned. # def complete(id, opt, icase = false, *pat, &block) __send__(id).complete(opt, icase, *pat, &block) end def get_candidates(id) yield __send__(id).keys end # # Iterates over each option, passing the option to the +block+. # def each_option(&block) list.each(&block) end # # Creates the summary table, passing each line to the +block+ (without # newline). The arguments +args+ are passed along to the summarize # method which is called on every option. # def summarize(*args, &block) sum = [] list.reverse_each do |opt| if opt.respond_to?(:summarize) # perhaps Gem::OptionParser::Switch s = [] opt.summarize(*args) {|l| s << l} sum.concat(s.reverse) elsif !opt or opt.empty? sum << "" elsif opt.respond_to?(:each_line) sum.concat([*opt.each_line].reverse) else sum.concat([*opt.each].reverse) end end sum.reverse_each(&block) end def add_banner(to) # :nodoc: list.each do |opt| if opt.respond_to?(:add_banner) opt.add_banner(to) end end to end def compsys(*args, &block) # :nodoc: list.each do |opt| if opt.respond_to?(:compsys) opt.compsys(*args, &block) end end end end # # Hash with completion search feature. See Gem::OptionParser::Completion. # class CompletingHash < Hash include Completion # # Completion for hash key. # def match(key) *values = fetch(key) { raise AmbiguousArgument, catch(:ambiguous) {return complete(key)} } return key, *values end end # :stopdoc: # # Enumeration of acceptable argument styles. Possible values are: # # NO_ARGUMENT:: The switch takes no arguments. (:NONE) # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED) # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL) # # Use like --switch=argument (long style) or -Xargument (short style). For # short style, only portion matched to argument pattern is treated as # argument. # ArgumentStyle = {} NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument} RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument} OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument} ArgumentStyle.freeze # # Switches common used such as '--', and also provides default # argument classes # DefaultList = List.new DefaultList.short['-'] = Switch::NoArgument.new {} DefaultList.long[''] = Switch::NoArgument.new {throw :terminate} COMPSYS_HEADER = <<'XXX' # :nodoc: typeset -A opt_args local context state line _arguments -s -S \ XXX def compsys(to, name = File.basename($0)) # :nodoc: to << "#compdef #{name}\n" to << COMPSYS_HEADER visit(:compsys, {}, {}) {|o, d| to << %Q[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n] } to << " '*:file:_files' && return 0\n" end # # Default options for ARGV, which never appear in option summary. # Officious = {} # # --help # Shows option summary. # Officious['help'] = proc do |parser| Switch::NoArgument.new do |arg| puts parser.help exit end end # # --*-completion-bash=WORD # Shows candidates for command line completion. # Officious['*-completion-bash'] = proc do |parser| Switch::RequiredArgument.new do |arg| puts parser.candidate(arg) exit end end # # --*-completion-zsh[=NAME:FILE] # Creates zsh completion file. # Officious['*-completion-zsh'] = proc do |parser| Switch::OptionalArgument.new do |arg| parser.compsys(STDOUT, arg) exit end end # # --version # Shows version string if Version is defined. # Officious['version'] = proc do |parser| Switch::OptionalArgument.new do |pkg| if pkg begin require 'rubygems/optparse/lib/optparse/version' rescue LoadError else show_version(*pkg.split(/,/)) or abort("#{parser.program_name}: no version found in package #{pkg}") exit end end v = parser.ver or abort("#{parser.program_name}: version unknown") puts v exit end end # :startdoc: # # Class methods # # # Initializes a new instance and evaluates the optional block in context # of the instance. Arguments +args+ are passed to #new, see there for # description of parameters. # # This method is *deprecated*, its behavior corresponds to the older #new # method. # def self.with(*args, &block) opts = new(*args) opts.instance_eval(&block) opts end # # Returns an incremented value of +default+ according to +arg+. # def self.inc(arg, default = nil) case arg when Integer arg.nonzero? when nil default.to_i + 1 end end def inc(*args) self.class.inc(*args) end # # Initializes the instance and yields itself if called with a block. # # +banner+:: Banner message. # +width+:: Summary width. # +indent+:: Summary indent. # def initialize(banner = nil, width = 32, indent = ' ' * 4) @stack = [DefaultList, List.new, List.new] @program_name = nil @banner = banner @summary_width = width @summary_indent = indent @default_argv = ARGV @require_exact = false add_officious yield self if block_given? end def add_officious # :nodoc: list = base() Officious.each do |opt, block| list.long[opt] ||= block.call(self) end end # # Terminates option parsing. Optional parameter +arg+ is a string pushed # back to be the first non-option argument. # def terminate(arg = nil) self.class.terminate(arg) end def self.terminate(arg = nil) throw :terminate, arg end @stack = [DefaultList] def self.top() DefaultList end # # Directs to accept specified class +t+. The argument string is passed to # the block in which it should be converted to the desired class. # # +t+:: Argument class specifier, any object including Class. # +pat+:: Pattern for argument, defaults to +t+ if it responds to match. # # accept(t, pat, &block) # def accept(*args, &blk) top.accept(*args, &blk) end # # See #accept. # def self.accept(*args, &blk) top.accept(*args, &blk) end # # Directs to reject specified class argument. # # +t+:: Argument class specifier, any object including Class. # # reject(t) # def reject(*args, &blk) top.reject(*args, &blk) end # # See #reject. # def self.reject(*args, &blk) top.reject(*args, &blk) end # # Instance methods # # Heading banner preceding summary. attr_writer :banner # Program name to be emitted in error message and default banner, # defaults to $0. attr_writer :program_name # Width for option list portion of summary. Must be Numeric. attr_accessor :summary_width # Indentation for summary. Must be String (or have + String method). attr_accessor :summary_indent # Strings to be parsed in default. attr_accessor :default_argv # Whether to require that options match exactly (disallows providing # abbreviated long option as short option). attr_accessor :require_exact # # Heading banner preceding summary. # def banner unless @banner @banner = +"Usage: #{program_name} [options]" visit(:add_banner, @banner) end @banner end # # Program name to be emitted in error message and default banner, defaults # to $0. # def program_name @program_name || File.basename($0, '.*') end # for experimental cascading :-) alias set_banner banner= alias set_program_name program_name= alias set_summary_width summary_width= alias set_summary_indent summary_indent= # Version attr_writer :version # Release code attr_writer :release # # Version # def version (defined?(@version) && @version) || (defined?(::Version) && ::Version) end # # Release code # def release (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE) end # # Returns version string from program_name, version and release. # def ver if v = version str = +"#{program_name} #{[v].join('.')}" str << " (#{v})" if v = release str end end def warn(mesg = $!) super("#{program_name}: #{mesg}") end def abort(mesg = $!) super("#{program_name}: #{mesg}") end # # Subject of #on / #on_head, #accept / #reject # def top @stack[-1] end # # Subject of #on_tail. # def base @stack[1] end # # Pushes a new List. # def new @stack.push(List.new) if block_given? yield self else self end end # # Removes the last List. # def remove @stack.pop end # # Puts option summary into +to+ and returns +to+. Yields each line if # a block is given. # # +to+:: Output destination, which must have method <<. Defaults to []. # +width+:: Width of left side, defaults to @summary_width. # +max+:: Maximum length allowed for left side, defaults to +width+ - 1. # +indent+:: Indentation, defaults to @summary_indent. # def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk) nl = "\n" blk ||= proc {|l| to << (l.index(nl, -1) ? l : l + nl)} visit(:summarize, {}, {}, width, max, indent, &blk) to end # # Returns option summary string. # def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end alias to_s help # # Returns option summary list. # def to_a; summarize("#{banner}".split(/^/)) end # # Checks if an argument is given twice, in which case an ArgumentError is # raised. Called from Gem::OptionParser#switch only. # # +obj+:: New argument. # +prv+:: Previously specified argument. # +msg+:: Exception message. # def notwice(obj, prv, msg) # :nodoc: unless !prv or prv == obj raise(ArgumentError, "argument #{msg} given twice: #{obj}", ParseError.filter_backtrace(caller(2))) end obj end private :notwice SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc: # :call-seq: # make_switch(params, block = nil) # # :include: ../doc/optparse/creates_option.rdoc # def make_switch(opts, block = nil) short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], [] ldesc, sdesc, desc, arg = [], [], [] default_style = Switch::NoArgument default_pattern = nil klass = nil q, a = nil has_arg = false opts.each do |o| # argument class next if search(:atype, o) do |pat, c| klass = notwice(o, klass, 'type') if not_style and not_style != Switch::NoArgument not_pattern, not_conv = pat, c else default_pattern, conv = pat, c end end # directly specified pattern(any object possible to match) if (!(String === o || Symbol === o)) and o.respond_to?(:match) pattern = notwice(o, pattern, 'pattern') if pattern.respond_to?(:convert) conv = pattern.method(:convert).to_proc else conv = SPLAT_PROC end next end # anything others case o when Proc, Method block = notwice(o, block, 'block') when Array, Hash case pattern when CompletingHash when nil pattern = CompletingHash.new conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert) else raise ArgumentError, "argument pattern given twice" end o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}} when Module raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4)) when *ArgumentStyle.keys style = notwice(ArgumentStyle[o], style, 'style') when /^--no-([^\[\]=\s]*)(.+)?/ q, a = $1, $2 o = notwice(a ? Object : TrueClass, klass, 'type') not_pattern, not_conv = search(:atype, o) unless not_style not_style = (not_style || default_style).guess(arg = a) if a default_style = Switch::NoArgument default_pattern, conv = search(:atype, FalseClass) unless default_pattern ldesc << "--no-#{q}" (q = q.downcase).tr!('_', '-') long << "no-#{q}" nolong << q when /^--\[no-\]([^\[\]=\s]*)(.+)?/ q, a = $1, $2 o = notwice(a ? Object : TrueClass, klass, 'type') if a default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern end ldesc << "--[no-]#{q}" (o = q.downcase).tr!('_', '-') long << o not_pattern, not_conv = search(:atype, FalseClass) unless not_style not_style = Switch::NoArgument nolong << "no-#{o}" when /^--([^\[\]=\s]*)(.+)?/ q, a = $1, $2 if a o = notwice(NilClass, klass, 'type') default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern end ldesc << "--#{q}" (o = q.downcase).tr!('_', '-') long << o when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/ q, a = $1, $2 o = notwice(Object, klass, 'type') if a default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern else has_arg = true end sdesc << "-#{q}" short << Regexp.new(q) when /^-(.)(.+)?/ q, a = $1, $2 if a o = notwice(NilClass, klass, 'type') default_style = default_style.guess(arg = a) default_pattern, conv = search(:atype, o) unless default_pattern end sdesc << "-#{q}" short << q when /^=/ style = notwice(default_style.guess(arg = o), style, 'style') default_pattern, conv = search(:atype, Object) unless default_pattern else desc.push(o) end end default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern if !(short.empty? and long.empty?) if has_arg and default_style == Switch::NoArgument default_style = Switch::RequiredArgument end s = (style || default_style).new(pattern || default_pattern, conv, sdesc, ldesc, arg, desc, block) elsif !block if style or pattern raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller) end s = desc else short << pattern s = (style || default_style).new(pattern, conv, nil, nil, arg, desc, block) end return s, short, long, (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style), nolong end # :call-seq: # define(*params, &block) # # :include: ../doc/optparse/creates_option.rdoc # def define(*opts, &block) top.append(*(sw = make_switch(opts, block))) sw[0] end # :call-seq: # on(*params, &block) # # :include: ../doc/optparse/creates_option.rdoc # def on(*opts, &block) define(*opts, &block) self end alias def_option define # :call-seq: # define_head(*params, &block) # # :include: ../doc/optparse/creates_option.rdoc # def define_head(*opts, &block) top.prepend(*(sw = make_switch(opts, block))) sw[0] end # :call-seq: # on_head(*params, &block) # # :include: ../doc/optparse/creates_option.rdoc # # The new option is added at the head of the summary. # def on_head(*opts, &block) define_head(*opts, &block) self end alias def_head_option define_head # :call-seq: # define_tail(*params, &block) # # :include: ../doc/optparse/creates_option.rdoc # def define_tail(*opts, &block) base.append(*(sw = make_switch(opts, block))) sw[0] end # # :call-seq: # on_tail(*params, &block) # # :include: ../doc/optparse/creates_option.rdoc # # The new option is added at the tail of the summary. # def on_tail(*opts, &block) define_tail(*opts, &block) self end alias def_tail_option define_tail # # Add separator in summary. # def separator(string) top.append(string, nil, nil) end # # Parses command line arguments +argv+ in order. When a block is given, # each non-option argument is yielded. When optional +into+ keyword # argument is provided, the parsed option values are stored there via # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other # similar object). # # Returns the rest of +argv+ left unparsed. # def order(*argv, into: nil, &nonopt) argv = argv[0].dup if argv.size == 1 and Array === argv[0] order!(argv, into: into, &nonopt) end # # Same as #order, but removes switches destructively. # Non-option arguments remain in +argv+. # def order!(argv = default_argv, into: nil, &nonopt) setter = ->(name, val) {into[name.to_sym] = val} if into parse_in_order(argv, setter, &nonopt) end def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc: opt, arg, val, rest = nil nonopt ||= proc {|a| throw :terminate, a} argv.unshift(arg) if arg = catch(:terminate) { while arg = argv.shift case arg # long option when /\A--([^=]*)(?:=(.*))?/m opt, rest = $1, $2 opt.tr!('_', '-') begin sw, = complete(:long, opt, true) if require_exact && !sw.long.include?(arg) raise InvalidOption, arg end rescue ParseError raise $!.set_option(arg, true) end begin opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)} val = cb.call(val) if cb setter.call(sw.switch_name, val) if setter rescue ParseError raise $!.set_option(arg, rest) end # short option when /\A-(.)((=).*|.+)?/m eq, rest, opt = $3, $2, $1 has_arg, val = eq, rest begin sw, = search(:short, opt) unless sw begin sw, = complete(:short, opt) # short option matched. val = arg.delete_prefix('-') has_arg = true rescue InvalidOption raise if require_exact # if no short options match, try completion with long # options. sw, = complete(:long, opt) eq ||= !rest end end rescue ParseError raise $!.set_option(arg, true) end begin opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq} rescue ParseError raise $!.set_option(arg, arg.length > 2) else raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}" end begin argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-') val = cb.call(val) if cb setter.call(sw.switch_name, val) if setter rescue ParseError raise $!.set_option(arg, arg.length > 2) end # non-option argument else catch(:prune) do visit(:each_option) do |sw0| sw = sw0 sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg) end nonopt.call(arg) end end end nil } visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern} argv end private :parse_in_order # # Parses command line arguments +argv+ in permutation mode and returns # list of non-option arguments. When optional +into+ keyword # argument is provided, the parsed option values are stored there via # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other # similar object). # def permute(*argv, into: nil) argv = argv[0].dup if argv.size == 1 and Array === argv[0] permute!(argv, into: into) end # # Same as #permute, but removes switches destructively. # Non-option arguments remain in +argv+. # def permute!(argv = default_argv, into: nil) nonopts = [] order!(argv, into: into, &nonopts.method(:<<)) argv[0, 0] = nonopts argv end # # Parses command line arguments +argv+ in order when environment variable # POSIXLY_CORRECT is set, and in permutation mode otherwise. # When optional +into+ keyword argument is provided, the parsed option # values are stored there via <code>[]=</code> method (so it can be Hash, # or OpenStruct, or other similar object). # def parse(*argv, into: nil) argv = argv[0].dup if argv.size == 1 and Array === argv[0] parse!(argv, into: into) end # # Same as #parse, but removes switches destructively. # Non-option arguments remain in +argv+. # def parse!(argv = default_argv, into: nil) if ENV.include?('POSIXLY_CORRECT') order!(argv, into: into) else permute!(argv, into: into) end end # # Wrapper method for getopts.rb. # # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option") # # params["a"] = true # -a # # params["b"] = "1" # -b1 # # params["foo"] = "1" # --foo # # params["bar"] = "x" # --bar x # # params["zot"] = "z" # --zot Z # def getopts(*args) argv = Array === args.first ? args.shift : default_argv single_options, *long_options = *args result = {} single_options.scan(/(.)(:)?/) do |opt, val| if val result[opt] = nil define("-#{opt} VAL") else result[opt] = false define("-#{opt}") end end if single_options long_options.each do |arg| arg, desc = arg.split(';', 2) opt, val = arg.split(':', 2) if val result[opt] = val.empty? ? nil : val define("--#{opt}=#{result[opt] || "VAL"}", *[desc].compact) else result[opt] = false define("--#{opt}", *[desc].compact) end end parse_in_order(argv, result.method(:[]=)) result end # # See #getopts. # def self.getopts(*args) new.getopts(*args) end # # Traverses @stack, sending each element method +id+ with +args+ and # +block+. # def visit(id, *args, &block) # :nodoc: @stack.reverse_each do |el| el.__send__(id, *args, &block) end nil end private :visit # # Searches +key+ in @stack for +id+ hash and returns or yields the result. # def search(id, key) # :nodoc: block_given = block_given? visit(:search, id, key) do |k| return block_given ? yield(k) : k end end private :search # # Completes shortened long style option switch and returns pair of # canonical switch and switch descriptor Gem::OptionParser::Switch. # # +typ+:: Searching table. # +opt+:: Searching key. # +icase+:: Search case insensitive if true. # +pat+:: Optional pattern for completion. # def complete(typ, opt, icase = false, *pat) # :nodoc: if pat.empty? search(typ, opt) {|sw| return [sw, opt]} # exact match or... end ambiguous = catch(:ambiguous) { visit(:complete, typ, opt, icase, *pat) {|o, *sw| return sw} } exc = ambiguous ? AmbiguousOption : InvalidOption raise exc.new(opt, additional: self.method(:additional_message).curry[typ]) end private :complete # # Returns additional info. # def additional_message(typ, opt) return unless typ and opt and defined?(DidYouMean::SpellChecker) all_candidates = [] visit(:get_candidates, typ) do |candidates| all_candidates.concat(candidates) end all_candidates.select! {|cand| cand.is_a?(String) } checker = DidYouMean::SpellChecker.new(dictionary: all_candidates) suggestions = all_candidates & checker.correct(opt) if DidYouMean.respond_to?(:formatter) DidYouMean.formatter.message_for(suggestions) else "\nDid you mean? #{suggestions.join("\n ")}" end end def candidate(word) list = [] case word when '-' long = short = true when /\A--/ word, arg = word.split(/=/, 2) argpat = Completion.regexp(arg, false) if arg and !arg.empty? long = true when /\A-/ short = true end pat = Completion.regexp(word, long) visit(:each_option) do |opt| next unless Switch === opt opts = (long ? opt.long : []) + (short ? opt.short : []) opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat if /\A=/ =~ opt.arg opts.map! {|sw| sw + "="} if arg and CompletingHash === opt.pattern if opts = opt.pattern.candidate(arg, false, argpat) opts.map!(&:last) end end end list.concat(opts) end list end # # Loads options from file names as +filename+. Does nothing when the file # is not present. Returns whether successfully loaded. # # +filename+ defaults to basename of the program without suffix in a # directory ~/.options, then the basename with '.options' suffix # under XDG and Haiku standard places. # def load(filename = nil) unless filename basename = File.basename($0, '.*') return true if load(File.expand_path(basename, '~/.options')) rescue nil basename << ".options" return [ # XDG ENV['XDG_CONFIG_HOME'], '~/.config', *ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR), # Haiku '~/config/settings', ].any? {|dir| next if !dir or dir.empty? load(File.expand_path(basename, dir)) rescue nil } end begin parse(*IO.readlines(filename).each {|s| s.chomp!}) true rescue Errno::ENOENT, Errno::ENOTDIR false end end # # Parses environment variable +env+ or its uppercase with splitting like a # shell. # # +env+ defaults to the basename of the program. # def environment(env = File.basename($0, '.*')) env = ENV[env] || ENV[env.upcase] or return require 'shellwords' parse(*Shellwords.shellwords(env)) end # # Acceptable argument classes # # # Any string and no conversion. This is fall-back. # accept(Object) {|s,|s or s.nil?} accept(NilClass) {|s,|s} # # Any non-empty string, and no conversion. # accept(String, /.+/m) {|s,*|s} # # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal # for 0x, and decimal for others; with optional sign prefix. Converts to # Integer. # decimal = '\d+(?:_\d+)*' binary = 'b[01]+(?:_[01]+)*' hex = 'x[\da-f]+(?:_[\da-f]+)*' octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?" integer = "#{octal}|#{decimal}" accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) {|s,| begin Integer(s) rescue ArgumentError raise Gem::OptionParser::InvalidArgument, s end if s } # # Float number format, and converts to Float. # float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?" floatpat = %r"\A[-+]?#{float}\z"io accept(Float, floatpat) {|s,| s.to_f if s} # # Generic numeric format, converts to Integer for integer format, Float # for float format, and Rational for rational format. # real = "[-+]?(?:#{octal}|#{float})" accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, f, n,| if n Rational(d, n) elsif f Float(s) else Integer(s) end } # # Decimal integer format, to be converted to Integer. # DecimalInteger = /\A[-+]?#{decimal}\z/io accept(DecimalInteger, DecimalInteger) {|s,| begin Integer(s, 10) rescue ArgumentError raise Gem::OptionParser::InvalidArgument, s end if s } # # Ruby/C like octal/hexadecimal/binary integer format, to be converted to # Integer. # OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io accept(OctalInteger, OctalInteger) {|s,| begin Integer(s, 8) rescue ArgumentError raise Gem::OptionParser::InvalidArgument, s end if s } # # Decimal integer/float number format, to be converted to Integer for # integer format, Float for float format. # DecimalNumeric = floatpat # decimal integer is allowed as float also. accept(DecimalNumeric, floatpat) {|s, f| begin if f Float(s) else Integer(s) end rescue ArgumentError raise Gem::OptionParser::InvalidArgument, s end if s } # # Boolean switch, which means whether it is present or not, whether it is # absent or not with prefix no-, or it takes an argument # yes/no/true/false/+/-. # yesno = CompletingHash.new %w[- no false].each {|el| yesno[el] = false} %w[+ yes true].each {|el| yesno[el] = true} yesno['nil'] = false # should be nil? accept(TrueClass, yesno) {|arg, val| val == nil or val} # # Similar to TrueClass, but defaults to false. # accept(FalseClass, yesno) {|arg, val| val != nil and val} # # List of strings separated by ",". # accept(Array) do |s, | if s s = s.split(',').collect {|ss| ss unless ss.empty?} end s end # # Regular expression with options. # accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o| f = 0 if o f |= Regexp::IGNORECASE if /i/ =~ o f |= Regexp::MULTILINE if /m/ =~ o f |= Regexp::EXTENDED if /x/ =~ o k = o.delete("imx") k = nil if k.empty? end Regexp.new(s || all, f, k) end # # Exceptions # # # Base class of exceptions from Gem::OptionParser. # class ParseError < RuntimeError # Reason which caused the error. Reason = 'parse error' def initialize(*args, additional: nil) @additional = additional @arg0, = args @args = args @reason = nil end attr_reader :args attr_writer :reason attr_accessor :additional # # Pushes back erred argument(s) to +argv+. # def recover(argv) argv[0, 0] = @args argv end def self.filter_backtrace(array) unless $DEBUG array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~)) end array end def set_backtrace(array) super(self.class.filter_backtrace(array)) end def set_option(opt, eq) if eq @args[0] = opt else @args.unshift(opt) end self end # # Returns error reason. Override this for I18N. # def reason @reason || self.class::Reason end def inspect "#<#{self.class}: #{args.join(' ')}>" end # # Default stringizing method to emit standard error message. # def message "#{reason}: #{args.join(' ')}#{additional[@arg0] if additional}" end alias to_s message end # # Raises when ambiguously completable string is encountered. # class AmbiguousOption < ParseError const_set(:Reason, 'ambiguous option') end # # Raises when there is an argument for a switch which takes no argument. # class NeedlessArgument < ParseError const_set(:Reason, 'needless argument') end # # Raises when a switch with mandatory argument has no argument. # class MissingArgument < ParseError const_set(:Reason, 'missing argument') end # # Raises when switch is undefined. # class InvalidOption < ParseError const_set(:Reason, 'invalid option') end # # Raises when the given argument does not match required format. # class InvalidArgument < ParseError const_set(:Reason, 'invalid argument') end # # Raises when the given argument word can't be completed uniquely. # class AmbiguousArgument < InvalidArgument const_set(:Reason, 'ambiguous argument') end # # Miscellaneous # # # Extends command line arguments array (ARGV) to parse itself. # module Arguable # # Sets Gem::OptionParser object, when +opt+ is +false+ or +nil+, methods # Gem::OptionParser::Arguable#options and Gem::OptionParser::Arguable#options= are # undefined. Thus, there is no ways to access the Gem::OptionParser object # via the receiver object. # def options=(opt) unless @optparse = opt class << self undef_method(:options) undef_method(:options=) end end end # # Actual Gem::OptionParser object, automatically created if nonexistent. # # If called with a block, yields the Gem::OptionParser object and returns the # result of the block. If an Gem::OptionParser::ParseError exception occurs # in the block, it is rescued, a error message printed to STDERR and # +nil+ returned. # def options @optparse ||= Gem::OptionParser.new @optparse.default_argv = self block_given? or return @optparse begin yield @optparse rescue ParseError @optparse.warn $! nil end end # # Parses +self+ destructively in order and returns +self+ containing the # rest arguments left unparsed. # def order!(&blk) options.order!(self, &blk) end # # Parses +self+ destructively in permutation mode and returns +self+ # containing the rest arguments left unparsed. # def permute!() options.permute!(self) end # # Parses +self+ destructively and returns +self+ containing the # rest arguments left unparsed. # def parse!() options.parse!(self) end # # Substitution of getopts is possible as follows. Also see # Gem::OptionParser#getopts. # # def getopts(*args) # ($OPT = ARGV.getopts(*args)).each do |opt, val| # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val" # end # rescue Gem::OptionParser::ParseError # end # def getopts(*args) options.getopts(self, *args) end # # Initializes instance variable. # def self.extend_object(obj) super obj.instance_eval {@optparse = nil} end def initialize(*args) super @optparse = nil end end # # Acceptable argument classes. Now contains DecimalInteger, OctalInteger # and DecimalNumeric. See Acceptable argument classes (in source code). # module Acceptables const_set(:DecimalInteger, Gem::OptionParser::DecimalInteger) const_set(:OctalInteger, Gem::OptionParser::OctalInteger) const_set(:DecimalNumeric, Gem::OptionParser::DecimalNumeric) end end # ARGV is arguable by Gem::OptionParser ARGV.extend(Gem::OptionParser::Arguable) rubygems/rubygems/resolver/api_specification.rb 0000644 00000005367 15102661023 0016104 0 ustar 00 # frozen_string_literal: true ## # Represents a specification retrieved via the rubygems.org API. # # This is used to avoid loading the full Specification object when all we need # is the name, version, and dependencies. class Gem::Resolver::APISpecification < Gem::Resolver::Specification ## # We assume that all instances of this class are immutable; # so avoid duplicated generation for performance. @@cache = {} def self.new(set, api_data) cache_key = [set, api_data] cache = @@cache[cache_key] return cache if cache @@cache[cache_key] = super end ## # Creates an APISpecification for the given +set+ from the rubygems.org # +api_data+. # # See https://guides.rubygems.org/rubygems-org-api/#misc_methods for the # format of the +api_data+. def initialize(set, api_data) super() @set = set @name = api_data[:name] @version = Gem::Version.new(api_data[:number]).freeze @platform = Gem::Platform.new(api_data[:platform]).freeze @original_platform = api_data[:platform].freeze @dependencies = api_data[:dependencies].map do |name, ver| Gem::Dependency.new(name, ver.split(/\s*,\s*/)).freeze end.freeze @required_ruby_version = Gem::Requirement.new(api_data.dig(:requirements, :ruby)).freeze @required_rubygems_version = Gem::Requirement.new(api_data.dig(:requirements, :rubygems)).freeze end def ==(other) # :nodoc: self.class === other and @set == other.set and @name == other.name and @version == other.version and @platform == other.platform end def hash @set.hash ^ @name.hash ^ @version.hash ^ @platform.hash end def fetch_development_dependencies # :nodoc: spec = source.fetch_spec Gem::NameTuple.new @name, @version, @platform @dependencies = spec.dependencies end def installable_platform? # :nodoc: Gem::Platform.match_gem? @platform, @name end def pretty_print(q) # :nodoc: q.group 2, '[APISpecification', ']' do q.breakable q.text "name: #{name}" q.breakable q.text "version: #{version}" q.breakable q.text "platform: #{platform}" q.breakable q.text 'dependencies:' q.breakable q.pp @dependencies q.breakable q.text "set uri: #{@set.dep_uri}" end end ## # Fetches a Gem::Specification for this APISpecification. def spec # :nodoc: @spec ||= begin tuple = Gem::NameTuple.new @name, @version, @platform source.fetch_spec tuple rescue Gem::RemoteFetcher::FetchError raise if @original_platform == @platform tuple = Gem::NameTuple.new @name, @version, @original_platform source.fetch_spec tuple end end def source # :nodoc: @set.source end end rubygems/rubygems/resolver/specification.rb 0000644 00000005244 15102661023 0015245 0 ustar 00 # frozen_string_literal: true ## # A Resolver::Specification contains a subset of the information # contained in a Gem::Specification. Only the information necessary for # dependency resolution in the resolver is included. class Gem::Resolver::Specification ## # The dependencies of the gem for this specification attr_reader :dependencies ## # The name of the gem for this specification attr_reader :name ## # The platform this gem works on. attr_reader :platform ## # The set this specification came from. attr_reader :set ## # The source for this specification attr_reader :source ## # The Gem::Specification for this Resolver::Specification. # # Implementers, note that #install updates @spec, so be sure to cache the # Gem::Specification in @spec when overriding. attr_reader :spec ## # The version of the gem for this specification. attr_reader :version ## # The required_ruby_version constraint for this specification. attr_reader :required_ruby_version ## # The required_ruby_version constraint for this specification. attr_reader :required_rubygems_version ## # Sets default instance variables for the specification. def initialize @dependencies = nil @name = nil @platform = nil @set = nil @source = nil @version = nil @required_ruby_version = Gem::Requirement.default @required_rubygems_version = Gem::Requirement.default end ## # Fetches development dependencies if the source does not provide them by # default (see APISpecification). def fetch_development_dependencies # :nodoc: end ## # The name and version of the specification. # # Unlike Gem::Specification#full_name, the platform is not included. def full_name "#{@name}-#{@version}" end ## # Installs this specification using the Gem::Installer +options+. The # install method yields a Gem::Installer instance, which indicates the # gem will be installed, or +nil+, which indicates the gem is already # installed. # # After installation #spec is updated to point to the just-installed # specification. def install(options = {}) require_relative '../installer' gem = download options installer = Gem::Installer.at gem, options yield installer if block_given? @spec = installer.install end def download(options) dir = options[:install_dir] || Gem.dir Gem.ensure_gem_subdirectories dir source.download spec, dir end ## # Returns true if this specification is installable on this platform. def installable_platform? Gem::Platform.match_spec? spec end def local? # :nodoc: false end end rubygems/rubygems/resolver/index_set.rb 0000644 00000002645 15102661023 0014411 0 ustar 00 # frozen_string_literal: true ## # The global rubygems pool represented via the traditional # source index. class Gem::Resolver::IndexSet < Gem::Resolver::Set def initialize(source = nil) # :nodoc: super() @f = if source sources = Gem::SourceList.from [source] Gem::SpecFetcher.new sources else Gem::SpecFetcher.fetcher end @all = Hash.new {|h,k| h[k] = [] } list, errors = @f.available_specs :complete @errors.concat errors list.each do |uri, specs| specs.each do |n| @all[n.name] << [uri, n] end end @specs = {} end ## # Return an array of IndexSpecification objects matching # DependencyRequest +req+. def find_all(req) res = [] return res unless @remote name = req.dependency.name @all[name].each do |uri, n| if req.match? n, @prerelease res << Gem::Resolver::IndexSpecification.new( self, n.name, n.version, uri, n.platform) end end res end def pretty_print(q) # :nodoc: q.group 2, '[IndexSet', ']' do q.breakable q.text 'sources:' q.breakable q.pp @f.sources q.breakable q.text 'specs:' q.breakable names = @all.values.map do |tuples| tuples.map do |_, tuple| tuple.full_name end end.flatten q.seplist names do |name| q.text name end end end end rubygems/rubygems/resolver/dependency_request.rb 0000644 00000004401 15102661023 0016305 0 ustar 00 # frozen_string_literal: true ## # Used Internally. Wraps a Dependency object to also track which spec # contained the Dependency. class Gem::Resolver::DependencyRequest ## # The wrapped Gem::Dependency attr_reader :dependency ## # The request for this dependency. attr_reader :requester ## # Creates a new DependencyRequest for +dependency+ from +requester+. # +requester may be nil if the request came from a user. def initialize(dependency, requester) @dependency = dependency @requester = requester end def ==(other) # :nodoc: case other when Gem::Dependency @dependency == other when Gem::Resolver::DependencyRequest @dependency == other.dependency else false end end ## # Is this dependency a development dependency? def development? @dependency.type == :development end ## # Does this dependency request match +spec+? # # NOTE: #match? only matches prerelease versions when #dependency is a # prerelease dependency. def match?(spec, allow_prerelease = false) @dependency.match? spec, nil, allow_prerelease end ## # Does this dependency request match +spec+? # # NOTE: #matches_spec? matches prerelease versions. See also #match? def matches_spec?(spec) @dependency.matches_spec? spec end ## # The name of the gem this dependency request is requesting. def name @dependency.name end def type @dependency.type end ## # Indicate that the request is for a gem explicitly requested by the user def explicit? @requester.nil? end ## # Indicate that the request is for a gem requested as a dependency of # another gem def implicit? !explicit? end ## # Return a String indicating who caused this request to be added (only # valid for implicit requests) def request_context @requester ? @requester.request : "(unknown)" end def pretty_print(q) # :nodoc: q.group 2, '[Dependency request ', ']' do q.breakable q.text @dependency.to_s q.breakable q.text ' requested by ' q.pp @requester end end ## # The version requirement for this dependency request def requirement @dependency.requirement end def to_s # :nodoc: @dependency.to_s end end rubygems/rubygems/resolver/vendor_set.rb 0000644 00000003646 15102661023 0014601 0 ustar 00 # frozen_string_literal: true ## # A VendorSet represents gems that have been unpacked into a specific # directory that contains a gemspec. # # This is used for gem dependency file support. # # Example: # # set = Gem::Resolver::VendorSet.new # # set.add_vendor_gem 'rake', 'vendor/rake' # # The directory vendor/rake must contain an unpacked rake gem along with a # rake.gemspec (watching the given name). class Gem::Resolver::VendorSet < Gem::Resolver::Set ## # The specifications for this set. attr_reader :specs # :nodoc: def initialize # :nodoc: super() @directories = {} @specs = {} end ## # Adds a specification to the set with the given +name+ which has been # unpacked into the given +directory+. def add_vendor_gem(name, directory) # :nodoc: gemspec = File.join directory, "#{name}.gemspec" spec = Gem::Specification.load gemspec raise Gem::GemNotFoundException, "unable to find #{gemspec} for gem #{name}" unless spec spec.full_gem_path = File.expand_path directory @specs[spec.name] = spec @directories[spec] = directory spec end ## # Returns an Array of VendorSpecification objects matching the # DependencyRequest +req+. def find_all(req) @specs.values.select do |spec| req.match? spec end.map do |spec| source = Gem::Source::Vendor.new @directories[spec] Gem::Resolver::VendorSpecification.new self, spec, source end end ## # Loads a spec with the given +name+. +version+, +platform+ and +source+ are # ignored. def load_spec(name, version, platform, source) # :nodoc: @specs.fetch name end def pretty_print(q) # :nodoc: q.group 2, '[VendorSet', ']' do next if @directories.empty? q.breakable dirs = @directories.map do |spec, directory| "#{spec.full_name}: #{directory}" end q.seplist dirs do |dir| q.text dir end end end end rubygems/rubygems/resolver/stats.rb 0000644 00000001675 15102661023 0013567 0 ustar 00 # frozen_string_literal: true class Gem::Resolver::Stats def initialize @max_depth = 0 @max_requirements = 0 @requirements = 0 @backtracking = 0 @iterations = 0 end def record_depth(stack) if stack.size > @max_depth @max_depth = stack.size end end def record_requirements(reqs) if reqs.size > @max_requirements @max_requirements = reqs.size end end def requirement! @requirements += 1 end def backtracking! @backtracking += 1 end def iteration! @iterations += 1 end PATTERN = "%20s: %d\n".freeze def display $stdout.puts "=== Resolver Statistics ===" $stdout.printf PATTERN, "Max Depth", @max_depth $stdout.printf PATTERN, "Total Requirements", @requirements $stdout.printf PATTERN, "Max Requirements", @max_requirements $stdout.printf PATTERN, "Backtracking #", @backtracking $stdout.printf PATTERN, "Iteration #", @iterations end end rubygems/rubygems/resolver/lock_set.rb 0000644 00000003252 15102661023 0014225 0 ustar 00 # frozen_string_literal: true ## # A set of gems from a gem dependencies lockfile. class Gem::Resolver::LockSet < Gem::Resolver::Set attr_reader :specs # :nodoc: ## # Creates a new LockSet from the given +sources+ def initialize(sources) super() @sources = sources.map do |source| Gem::Source::Lock.new source end @specs = [] end ## # Creates a new IndexSpecification in this set using the given +name+, # +version+ and +platform+. # # The specification's set will be the current set, and the source will be # the current set's source. def add(name, version, platform) # :nodoc: version = Gem::Version.new version specs = [ Gem::Resolver::LockSpecification.new(self, name, version, @sources, platform), ] @specs.concat specs specs end ## # Returns an Array of IndexSpecification objects matching the # DependencyRequest +req+. def find_all(req) @specs.select do |spec| req.match? spec end end ## # Loads a Gem::Specification with the given +name+, +version+ and # +platform+. +source+ is ignored. def load_spec(name, version, platform, source) # :nodoc: dep = Gem::Dependency.new name, version found = @specs.find do |spec| dep.matches_spec? spec and spec.platform == platform end tuple = Gem::NameTuple.new found.name, found.version, found.platform found.source.fetch_spec tuple end def pretty_print(q) # :nodoc: q.group 2, '[LockSet', ']' do q.breakable q.text 'source:' q.breakable q.pp @source q.breakable q.text 'specs:' q.breakable q.pp @specs.map {|spec| spec.full_name } end end end rubygems/rubygems/resolver/source_set.rb 0000644 00000001604 15102661023 0014574 0 ustar 00 ## # The SourceSet chooses the best available method to query a remote index. # # Kind off like BestSet but filters the sources for gems class Gem::Resolver::SourceSet < Gem::Resolver::Set ## # Creates a SourceSet for the given +sources+ or Gem::sources if none are # specified. +sources+ must be a Gem::SourceList. def initialize super() @links = {} @sets = {} end def find_all(req) # :nodoc: if set = get_set(req.dependency.name) set.find_all req else [] end end # potentially no-op def prefetch(reqs) # :nodoc: reqs.each do |req| if set = get_set(req.dependency.name) set.prefetch reqs end end end def add_source_gem(name, source) @links[name] = source end private def get_set(name) link = @links[name] @sets[link] ||= Gem::Source.new(link).dependency_resolver_set if link end end rubygems/rubygems/resolver/composed_set.rb 0000644 00000002274 15102661023 0015111 0 ustar 00 # frozen_string_literal: true ## # A ComposedSet allows multiple sets to be queried like a single set. # # To create a composed set with any number of sets use: # # Gem::Resolver.compose_sets set1, set2 # # This method will eliminate nesting of composed sets. class Gem::Resolver::ComposedSet < Gem::Resolver::Set attr_reader :sets # :nodoc: ## # Creates a new ComposedSet containing +sets+. Use # Gem::Resolver::compose_sets instead. def initialize(*sets) super() @sets = sets end ## # When +allow_prerelease+ is set to +true+ prereleases gems are allowed to # match dependencies. def prerelease=(allow_prerelease) super sets.each do |set| set.prerelease = allow_prerelease end end ## # Sets the remote network access for all composed sets. def remote=(remote) super @sets.each {|set| set.remote = remote } end def errors @errors + @sets.map {|set| set.errors }.flatten end ## # Finds all specs matching +req+ in all sets. def find_all(req) @sets.map do |s| s.find_all req end.flatten end ## # Prefetches +reqs+ in all sets. def prefetch(reqs) @sets.each {|s| s.prefetch(reqs) } end end rubygems/rubygems/resolver/molinillo/LICENSE 0000644 00000002155 15102661023 0015101 0 ustar 00 This project is licensed under the MIT license. Copyright (c) 2014 Samuel E. Giddins segiddins@segiddins.me Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rubygems/rubygems/resolver/molinillo/lib/molinillo/errors.rb 0000644 00000013602 15102661023 0020500 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo # An error that occurred during the resolution process class ResolverError < StandardError; end # An error caused by searching for a dependency that is completely unknown, # i.e. has no versions available whatsoever. class NoSuchDependencyError < ResolverError # @return [Object] the dependency that could not be found attr_accessor :dependency # @return [Array<Object>] the specifications that depended upon {#dependency} attr_accessor :required_by # Initializes a new error with the given missing dependency. # @param [Object] dependency @see {#dependency} # @param [Array<Object>] required_by @see {#required_by} def initialize(dependency, required_by = []) @dependency = dependency @required_by = required_by.uniq super() end # The error message for the missing dependency, including the specifications # that had this dependency. def message sources = required_by.map { |r| "`#{r}`" }.join(' and ') message = "Unable to find a specification for `#{dependency}`" message += " depended upon by #{sources}" unless sources.empty? message end end # An error caused by attempting to fulfil a dependency that was circular # # @note This exception will be thrown if and only if a {Vertex} is added to a # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an # existing {DependencyGraph::Vertex} class CircularDependencyError < ResolverError # [Set<Object>] the dependencies responsible for causing the error attr_reader :dependencies # Initializes a new error with the given circular vertices. # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency # that caused the error def initialize(vertices) super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}" @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set end end # An error caused by conflicts in version class VersionConflict < ResolverError # @return [{String => Resolution::Conflict}] the conflicts that caused # resolution to fail attr_reader :conflicts # @return [SpecificationProvider] the specification provider used during # resolution attr_reader :specification_provider # Initializes a new error with the given version conflicts. # @param [{String => Resolution::Conflict}] conflicts see {#conflicts} # @param [SpecificationProvider] specification_provider see {#specification_provider} def initialize(conflicts, specification_provider) pairs = [] conflicts.values.flat_map(&:requirements).each do |conflicting| conflicting.each do |source, conflict_requirements| conflict_requirements.each do |c| pairs << [c, source] end end end super "Unable to satisfy the following requirements:\n\n" \ "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}" @conflicts = conflicts @specification_provider = specification_provider end require_relative 'delegates/specification_provider' include Delegates::SpecificationProvider # @return [String] An error message that includes requirement trees, # which is much more detailed & customizable than the default message # @param [Hash] opts the options to create a message with. # @option opts [String] :solver_name The user-facing name of the solver # @option opts [String] :possibility_type The generic name of a possibility # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements # @option opts [Proc] :additional_message_for_conflict A proc that appends additional # messages for each conflict # @option opts [Proc] :version_for_spec A proc that returns the version number for a # possibility def message_with_trees(opts = {}) solver_name = opts.delete(:solver_name) { self.class.name.split('::').first } possibility_type = opts.delete(:possibility_type) { 'possibility named' } reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } } printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } } additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} } version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) } incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do proc do |name, _conflict| %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":) end end conflicts.sort.reduce(''.dup) do |o, (name, conflict)| o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n" if conflict.locked_requirement o << %( In snapshot (#{name_for_locking_dependency_source}):\n) o << %( #{printable_requirement.call(conflict.locked_requirement)}\n) o << %(\n) end o << %( In #{name_for_explicit_dependency_source}:\n) trees = reduce_trees.call(conflict.requirement_trees) o << trees.map do |tree| t = ''.dup depth = 2 tree.each do |req| t << ' ' * depth << printable_requirement.call(req) unless tree.last == req if spec = conflict.activated_by_name[name_for(req)] t << %( was resolved to #{version_for_spec.call(spec)}, which) end t << %( depends on) end t << %(\n) depth += 1 end t end.join("\n") additional_message_for_conflict.call(o, name, conflict) o end.strip end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/state.rb 0000644 00000003456 15102661023 0020312 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo # A state that a {Resolution} can be in # @attr [String] name the name of the current requirement # @attr [Array<Object>] requirements currently unsatisfied requirements # @attr [DependencyGraph] activated the graph of activated dependencies # @attr [Object] requirement the current requirement # @attr [Object] possibilities the possibilities to satisfy the current requirement # @attr [Integer] depth the depth of the resolution # @attr [Hash] conflicts unresolved conflicts, indexed by dependency name # @attr [Array<UnwindDetails>] unused_unwind_options unwinds for previous conflicts that weren't explored ResolutionState = Struct.new( :name, :requirements, :activated, :requirement, :possibilities, :depth, :conflicts, :unused_unwind_options ) class ResolutionState # Returns an empty resolution state # @return [ResolutionState] an empty state def self.empty new(nil, [], DependencyGraph.new, nil, nil, 0, {}, []) end end # A state that encapsulates a set of {#requirements} with an {Array} of # possibilities class DependencyState < ResolutionState # Removes a possibility from `self` # @return [PossibilityState] a state with a single possibility, # the possibility that was removed from `self` def pop_possibility_state PossibilityState.new( name, requirements.dup, activated, requirement, [possibilities.pop], depth + 1, conflicts.dup, unused_unwind_options.dup ).tap do |state| state.activated.tag(state) end end end # A state that encapsulates a single possibility to fulfill the given # {#requirement} class PossibilityState < ResolutionState end end rubygems/rubygems/resolver/molinillo/lib/molinillo/resolution.rb 0000644 00000103243 15102661023 0021370 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo class Resolver # A specific resolution from a given {Resolver} class Resolution # A conflict that the resolution process encountered # @attr [Object] requirement the requirement that immediately led to the conflict # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict # @attr [Object, nil] existing the existing spec that was in conflict with # the {#possibility} # @attr [Object] possibility_set the set of specs that was unable to be # activated due to a conflict. # @attr [Object] locked_requirement the relevant locking requirement. # @attr [Array<Array<Object>>] requirement_trees the different requirement # trees that led to every requirement for the conflicting name. # @attr [{String=>Object}] activated_by_name the already-activated specs. # @attr [Object] underlying_error an error that has occurred during resolution, and # will be raised at the end of it if no resolution is found. Conflict = Struct.new( :requirement, :requirements, :existing, :possibility_set, :locked_requirement, :requirement_trees, :activated_by_name, :underlying_error ) class Conflict # @return [Object] a spec that was unable to be activated due to a conflict def possibility possibility_set && possibility_set.latest_version end end # A collection of possibility states that share the same dependencies # @attr [Array] dependencies the dependencies for this set of possibilities # @attr [Array] possibilities the possibilities PossibilitySet = Struct.new(:dependencies, :possibilities) class PossibilitySet # String representation of the possibility set, for debugging def to_s "[#{possibilities.join(', ')}]" end # @return [Object] most up-to-date dependency in the possibility set def latest_version possibilities.last end end # Details of the state to unwind to when a conflict occurs, and the cause of the unwind # @attr [Integer] state_index the index of the state to unwind to # @attr [Object] state_requirement the requirement of the state we're unwinding to # @attr [Array] requirement_tree for the requirement we're relaxing # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict # @attr [Array] requirement_trees for the conflict # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind UnwindDetails = Struct.new( :state_index, :state_requirement, :requirement_tree, :conflicting_requirements, :requirement_trees, :requirements_unwound_to_instead ) class UnwindDetails include Comparable # We compare UnwindDetails when choosing which state to unwind to. If # two options have the same state_index we prefer the one most # removed from a requirement that caused the conflict. Both options # would unwind to the same state, but a `grandparent` option will # filter out fewer of its possibilities after doing so - where a state # is both a `parent` and a `grandparent` to requirements that have # caused a conflict this is the correct behaviour. # @param [UnwindDetail] other UnwindDetail to be compared # @return [Integer] integer specifying ordering def <=>(other) if state_index > other.state_index 1 elsif state_index == other.state_index reversed_requirement_tree_index <=> other.reversed_requirement_tree_index else -1 end end # @return [Integer] index of state requirement in reversed requirement tree # (the conflicting requirement itself will be at position 0) def reversed_requirement_tree_index @reversed_requirement_tree_index ||= if state_requirement requirement_tree.reverse.index(state_requirement) else 999_999 end end # @return [Boolean] where the requirement of the state we're unwinding # to directly caused the conflict. Note: in this case, it is # impossible for the state we're unwinding to to be a parent of # any of the other conflicting requirements (or we would have # circularity) def unwinding_to_primary_requirement? requirement_tree.last == state_requirement end # @return [Array] array of sub-dependencies to avoid when choosing a # new possibility for the state we've unwound to. Only relevant for # non-primary unwinds def sub_dependencies_to_avoid @requirements_to_avoid ||= requirement_trees.map do |tree| index = tree.index(state_requirement) tree[index + 1] if index end.compact end # @return [Array] array of all the requirements that led to the need for # this unwind def all_requirements @all_requirements ||= requirement_trees.flatten(1) end end # @return [SpecificationProvider] the provider that knows about # dependencies, requirements, specifications, versions, etc. attr_reader :specification_provider # @return [UI] the UI that knows how to communicate feedback about the # resolution process back to the user attr_reader :resolver_ui # @return [DependencyGraph] the base dependency graph to which # dependencies should be 'locked' attr_reader :base # @return [Array] the dependencies that were explicitly required attr_reader :original_requested # Initializes a new resolution. # @param [SpecificationProvider] specification_provider # see {#specification_provider} # @param [UI] resolver_ui see {#resolver_ui} # @param [Array] requested see {#original_requested} # @param [DependencyGraph] base see {#base} def initialize(specification_provider, resolver_ui, requested, base) @specification_provider = specification_provider @resolver_ui = resolver_ui @original_requested = requested @base = base @states = [] @iteration_counter = 0 @parents_of = Hash.new { |h, k| h[k] = [] } end # Resolves the {#original_requested} dependencies into a full dependency # graph # @raise [ResolverError] if successful resolution is impossible # @return [DependencyGraph] the dependency graph of successfully resolved # dependencies def resolve start_resolution while state break if !state.requirement && state.requirements.empty? indicate_progress if state.respond_to?(:pop_possibility_state) # DependencyState debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" } state.pop_possibility_state.tap do |s| if s states.push(s) activated.tag(s) end end end process_topmost_state end resolve_activated_specs ensure end_resolution end # @return [Integer] the number of resolver iterations in between calls to # {#resolver_ui}'s {UI#indicate_progress} method attr_accessor :iteration_rate private :iteration_rate # @return [Time] the time at which resolution began attr_accessor :started_at private :started_at # @return [Array<ResolutionState>] the stack of states for the resolution attr_accessor :states private :states private # Sets up the resolution process # @return [void] def start_resolution @started_at = Time.now push_initial_state debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" } resolver_ui.before_resolution end def resolve_activated_specs activated.vertices.each do |_, vertex| next unless vertex.payload latest_version = vertex.payload.possibilities.reverse_each.find do |possibility| vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) } end activated.set_payload(vertex.name, latest_version) end activated.freeze end # Ends the resolution process # @return [void] def end_resolution resolver_ui.after_resolution debug do "Finished resolution (#{@iteration_counter} steps) " \ "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})" end debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state end require_relative 'state' require_relative 'modules/specification_provider' require_relative 'delegates/resolution_state' require_relative 'delegates/specification_provider' include Gem::Resolver::Molinillo::Delegates::ResolutionState include Gem::Resolver::Molinillo::Delegates::SpecificationProvider # Processes the topmost available {RequirementState} on the stack # @return [void] def process_topmost_state if possibility attempt_to_activate else create_conflict unwind_for_conflict end rescue CircularDependencyError => underlying_error create_conflict(underlying_error) unwind_for_conflict end # @return [Object] the current possibility that the resolution is trying # to activate def possibility possibilities.last end # @return [RequirementState] the current state the resolution is # operating upon def state states.last end # Creates and pushes the initial state for the resolution, based upon the # {#requested} dependencies # @return [void] def push_initial_state graph = DependencyGraph.new.tap do |dg| original_requested.each do |requested| vertex = dg.add_vertex(name_for(requested), nil, true) vertex.explicit_requirements << requested end dg.tag(:initial_state) end push_state_for_requirements(original_requested, true, graph) end # Unwinds the states stack because a conflict has been encountered # @return [void] def unwind_for_conflict details_for_unwind = build_details_for_unwind unwind_options = unused_unwind_options debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" } conflicts.tap do |c| sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1) raise_error_unless_state(c) activated.rewind_to(sliced_states.first || :initial_state) if sliced_states state.conflicts = c state.unused_unwind_options = unwind_options filter_possibilities_after_unwind(details_for_unwind) index = states.size - 1 @parents_of.each { |_, a| a.reject! { |i| i >= index } } state.unused_unwind_options.reject! { |uw| uw.state_index >= index } end end # Raises a VersionConflict error, or any underlying error, if there is no # current state # @return [void] def raise_error_unless_state(conflicts) return if state error = conflicts.values.map(&:underlying_error).compact.first raise error || VersionConflict.new(conflicts, specification_provider) end # @return [UnwindDetails] Details of the nearest index to which we could unwind def build_details_for_unwind # Get the possible unwinds for the current conflict current_conflict = conflicts[name] binding_requirements = binding_requirements_for_conflict(current_conflict) unwind_details = unwind_options_for_requirements(binding_requirements) last_detail_for_current_unwind = unwind_details.sort.last current_detail = last_detail_for_current_unwind # Look for past conflicts that could be unwound to affect the # requirement tree for the current conflict all_reqs = last_detail_for_current_unwind.all_requirements all_reqs_size = all_reqs.size relevant_unused_unwinds = unused_unwind_options.select do |alternative| diff_reqs = all_reqs - alternative.requirements_unwound_to_instead next if diff_reqs.size == all_reqs_size # Find the highest index unwind whilst looping through current_detail = alternative if alternative > current_detail alternative end # Add the current unwind options to the `unused_unwind_options` array. # The "used" option will be filtered out during `unwind_for_conflict`. state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 } # Update the requirements_unwound_to_instead on any relevant unused unwinds relevant_unused_unwinds.each do |d| (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! end unwind_details.each do |d| (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! end current_detail end # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance # of resolving the passed requirements def unwind_options_for_requirements(binding_requirements) unwind_details = [] trees = [] binding_requirements.reverse_each do |r| partial_tree = [r] trees << partial_tree unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, []) # If this requirement has alternative possibilities, check if any would # satisfy the other requirements that created this conflict requirement_state = find_state_for(r) if conflict_fixing_possibilities?(requirement_state, binding_requirements) unwind_details << UnwindDetails.new( states.index(requirement_state), r, partial_tree, binding_requirements, trees, [] ) end # Next, look at the parent of this requirement, and check if the requirement # could have been avoided if an alternative PossibilitySet had been chosen parent_r = parent_of(r) next if parent_r.nil? partial_tree.unshift(parent_r) requirement_state = find_state_for(parent_r) if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) } unwind_details << UnwindDetails.new( states.index(requirement_state), parent_r, partial_tree, binding_requirements, trees, [] ) end # Finally, look at the grandparent and up of this requirement, looking # for any possibilities that wouldn't create their parent requirement grandparent_r = parent_of(parent_r) until grandparent_r.nil? partial_tree.unshift(grandparent_r) requirement_state = find_state_for(grandparent_r) if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) } unwind_details << UnwindDetails.new( states.index(requirement_state), grandparent_r, partial_tree, binding_requirements, trees, [] ) end parent_r = grandparent_r grandparent_r = parent_of(parent_r) end end unwind_details end # @param [DependencyState] state # @param [Array] binding_requirements array of requirements # @return [Boolean] whether or not the given state has any possibilities # that could satisfy the given requirements def conflict_fixing_possibilities?(state, binding_requirements) return false unless state state.possibilities.any? do |possibility_set| possibility_set.possibilities.any? do |poss| possibility_satisfies_requirements?(poss, binding_requirements) end end end # Filter's a state's possibilities to remove any that would not fix the # conflict we've just rewound from # @param [UnwindDetails] unwind_details details of the conflict just # unwound from # @return [void] def filter_possibilities_after_unwind(unwind_details) return unless state && !state.possibilities.empty? if unwind_details.unwinding_to_primary_requirement? filter_possibilities_for_primary_unwind(unwind_details) else filter_possibilities_for_parent_unwind(unwind_details) end end # Filter's a state's possibilities to remove any that would not satisfy # the requirements in the conflict we've just rewound from # @param [UnwindDetails] unwind_details details of the conflict just unwound from # @return [void] def filter_possibilities_for_primary_unwind(unwind_details) unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index } unwinds_to_state << unwind_details unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements) state.possibilities.reject! do |possibility_set| possibility_set.possibilities.none? do |poss| unwind_requirement_sets.any? do |requirements| possibility_satisfies_requirements?(poss, requirements) end end end end # @param [Object] possibility a single possibility # @param [Array] requirements an array of requirements # @return [Boolean] whether the possibility satisfies all of the # given requirements def possibility_satisfies_requirements?(possibility, requirements) name = name_for(possibility) activated.tag(:swap) activated.set_payload(name, possibility) if activated.vertex_named(name) satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) } activated.rewind_to(:swap) satisfied end # Filter's a state's possibilities to remove any that would (eventually) # create a requirement in the conflict we've just rewound from # @param [UnwindDetails] unwind_details details of the conflict just unwound from # @return [void] def filter_possibilities_for_parent_unwind(unwind_details) unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index } unwinds_to_state << unwind_details primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq parent_unwinds = unwinds_to_state.uniq - primary_unwinds allowed_possibility_sets = primary_unwinds.flat_map do |unwind| states[unwind.state_index].possibilities.select do |possibility_set| possibility_set.possibilities.any? do |poss| possibility_satisfies_requirements?(poss, unwind.conflicting_requirements) end end end requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid) state.possibilities.reject! do |possibility_set| !allowed_possibility_sets.include?(possibility_set) && (requirements_to_avoid - possibility_set.dependencies).empty? end end # @param [Conflict] conflict # @return [Array] minimal array of requirements that would cause the passed # conflict to occur. def binding_requirements_for_conflict(conflict) return [conflict.requirement] if conflict.possibility.nil? possible_binding_requirements = conflict.requirements.values.flatten(1).uniq # When there's a `CircularDependency` error the conflicting requirement # (the one causing the circular) won't be `conflict.requirement` # (which won't be for the right state, because we won't have created it, # because it's circular). # We need to make sure we have that requirement in the conflict's list, # otherwise we won't be able to unwind properly, so we just return all # the requirements for the conflict. return possible_binding_requirements if conflict.underlying_error possibilities = search_for(conflict.requirement) # If all the requirements together don't filter out all possibilities, # then the only two requirements we need to consider are the initial one # (where the dependency's version was first chosen) and the last if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities) return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact end # Loop through the possible binding requirements, removing each one # that doesn't bind. Use a `reverse_each` as we want the earliest set of # binding requirements, and don't use `reject!` as we wish to refine the # array *on each iteration*. binding_requirements = possible_binding_requirements.dup possible_binding_requirements.reverse_each do |req| next if req == conflict.requirement unless binding_requirement_in_set?(req, binding_requirements, possibilities) binding_requirements -= [req] end end binding_requirements end # @param [Object] requirement we wish to check # @param [Array] possible_binding_requirements array of requirements # @param [Array] possibilities array of possibilities the requirements will be used to filter # @return [Boolean] whether or not the given requirement is required to filter # out all elements of the array of possibilities. def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities) possibilities.any? do |poss| possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement]) end end # @param [Object] requirement # @return [Object] the requirement that led to `requirement` being added # to the list of requirements. def parent_of(requirement) return unless requirement return unless index = @parents_of[requirement].last return unless parent_state = @states[index] parent_state.requirement end # @param [String] name # @return [Object] the requirement that led to a version of a possibility # with the given name being activated. def requirement_for_existing_name(name) return nil unless vertex = activated.vertex_named(name) return nil unless vertex.payload states.find { |s| s.name == name }.requirement end # @param [Object] requirement # @return [ResolutionState] the state whose `requirement` is the given # `requirement`. def find_state_for(requirement) return nil unless requirement states.find { |i| requirement == i.requirement } end # @param [Object] underlying_error # @return [Conflict] a {Conflict} that reflects the failure to activate # the {#possibility} in conjunction with the current {#state} def create_conflict(underlying_error = nil) vertex = activated.vertex_named(name) locked_requirement = locked_requirement_named(name) requirements = {} unless vertex.explicit_requirements.empty? requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements end requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement vertex.incoming_edges.each do |edge| (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement) end activated_by_name = {} activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload } conflicts[name] = Conflict.new( requirement, requirements, vertex.payload && vertex.payload.latest_version, possibility, locked_requirement, requirement_trees, activated_by_name, underlying_error ) end # @return [Array<Array<Object>>] The different requirement # trees that led to every requirement for the current spec. def requirement_trees vertex = activated.vertex_named(name) vertex.requirements.map { |r| requirement_tree_for(r) } end # @param [Object] requirement # @return [Array<Object>] the list of requirements that led to # `requirement` being required. def requirement_tree_for(requirement) tree = [] while requirement tree.unshift(requirement) requirement = parent_of(requirement) end tree end # Indicates progress roughly once every second # @return [void] def indicate_progress @iteration_counter += 1 @progress_rate ||= resolver_ui.progress_rate if iteration_rate.nil? if Time.now - started_at >= @progress_rate self.iteration_rate = @iteration_counter end end if iteration_rate && (@iteration_counter % iteration_rate) == 0 resolver_ui.indicate_progress end end # Calls the {#resolver_ui}'s {UI#debug} method # @param [Integer] depth the depth of the {#states} stack # @param [Proc] block a block that yields a {#to_s} # @return [void] def debug(depth = 0, &block) resolver_ui.debug(depth, &block) end # Attempts to activate the current {#possibility} # @return [void] def attempt_to_activate debug(depth) { 'Attempting to activate ' + possibility.to_s } existing_vertex = activated.vertex_named(name) if existing_vertex.payload debug(depth) { "Found existing spec (#{existing_vertex.payload})" } attempt_to_filter_existing_spec(existing_vertex) else latest = possibility.latest_version possibility.possibilities.select! do |possibility| requirement_satisfied_by?(requirement, activated, possibility) end if possibility.latest_version.nil? # ensure there's a possibility for better error messages possibility.possibilities << latest if latest create_conflict unwind_for_conflict else activate_new_spec end end end # Attempts to update the existing vertex's `PossibilitySet` with a filtered version # @return [void] def attempt_to_filter_existing_spec(vertex) filtered_set = filtered_possibility_set(vertex) if !filtered_set.possibilities.empty? activated.set_payload(name, filtered_set) new_requirements = requirements.dup push_state_for_requirements(new_requirements, false) else create_conflict debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" } unwind_for_conflict end end # Generates a filtered version of the existing vertex's `PossibilitySet` using the # current state's `requirement` # @param [Object] vertex existing vertex # @return [PossibilitySet] filtered possibility set def filtered_possibility_set(vertex) PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities) end # @param [String] requirement_name the spec name to search for # @return [Object] the locked spec named `requirement_name`, if one # is found on {#base} def locked_requirement_named(requirement_name) vertex = base.vertex_named(requirement_name) vertex && vertex.payload end # Add the current {#possibility} to the dependency graph of the current # {#state} # @return [void] def activate_new_spec conflicts.delete(name) debug(depth) { "Activated #{name} at #{possibility}" } activated.set_payload(name, possibility) require_nested_dependencies_for(possibility) end # Requires the dependencies that the recently activated spec has # @param [Object] possibility_set the PossibilitySet that has just been # activated # @return [void] def require_nested_dependencies_for(possibility_set) nested_dependencies = dependencies_for(possibility_set.latest_version) debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" } nested_dependencies.each do |d| activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d) parent_index = states.size - 1 parents = @parents_of[d] parents << parent_index if parents.empty? end push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?) end # Pushes a new {DependencyState} that encapsulates both existing and new # requirements # @param [Array] new_requirements # @param [Boolean] requires_sort # @param [Object] new_activated # @return [void] def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated) new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort new_requirement = nil loop do new_requirement = new_requirements.shift break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement } end new_name = new_requirement ? name_for(new_requirement) : ''.freeze possibilities = possibilities_for_requirement(new_requirement) handle_missing_or_push_dependency_state DependencyState.new( new_name, new_requirements, new_activated, new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup ) end # Checks a proposed requirement with any existing locked requirement # before generating an array of possibilities for it. # @param [Object] requirement the proposed requirement # @param [Object] activated # @return [Array] possibilities def possibilities_for_requirement(requirement, activated = self.activated) return [] unless requirement if locked_requirement_named(name_for(requirement)) return locked_requirement_possibility_set(requirement, activated) end group_possibilities(search_for(requirement)) end # @param [Object] requirement the proposed requirement # @param [Object] activated # @return [Array] possibility set containing only the locked requirement, if any def locked_requirement_possibility_set(requirement, activated = self.activated) all_possibilities = search_for(requirement) locked_requirement = locked_requirement_named(name_for(requirement)) # Longwinded way to build a possibilities array with either the locked # requirement or nothing in it. Required, since the API for # locked_requirement isn't guaranteed. locked_possibilities = all_possibilities.select do |possibility| requirement_satisfied_by?(locked_requirement, activated, possibility) end group_possibilities(locked_possibilities) end # Build an array of PossibilitySets, with each element representing a group of # dependency versions that all have the same sub-dependency version constraints # and are contiguous. # @param [Array] possibilities an array of possibilities # @return [Array<PossibilitySet>] an array of possibility sets def group_possibilities(possibilities) possibility_sets = [] current_possibility_set = nil possibilities.reverse_each do |possibility| dependencies = dependencies_for(possibility) if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies) current_possibility_set.possibilities.unshift(possibility) else possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility])) current_possibility_set = possibility_sets.first end end possibility_sets end # Pushes a new {DependencyState}. # If the {#specification_provider} says to # {SpecificationProvider#allow_missing?} that particular requirement, and # there are no possibilities for that requirement, then `state` is not # pushed, and the vertex in {#activated} is removed, and we continue # resolving the remaining requirements. # @param [DependencyState] state # @return [void] def handle_missing_or_push_dependency_state(state) if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement) state.activated.detach_vertex_named(state.name) push_state_for_requirements(state.requirements.dup, false, state.activated) else states.push(state).tap { activated.tag(state) } end end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb 0000644 00000006320 15102661023 0025652 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo module Delegates # Delegates all {Gem::Resolver::Molinillo::SpecificationProvider} methods to a # `#specification_provider` property. module SpecificationProvider # (see Gem::Resolver::Molinillo::SpecificationProvider#search_for) def search_for(dependency) with_no_such_dependency_error_handling do specification_provider.search_for(dependency) end end # (see Gem::Resolver::Molinillo::SpecificationProvider#dependencies_for) def dependencies_for(specification) with_no_such_dependency_error_handling do specification_provider.dependencies_for(specification) end end # (see Gem::Resolver::Molinillo::SpecificationProvider#requirement_satisfied_by?) def requirement_satisfied_by?(requirement, activated, spec) with_no_such_dependency_error_handling do specification_provider.requirement_satisfied_by?(requirement, activated, spec) end end # (see Gem::Resolver::Molinillo::SpecificationProvider#dependencies_equal?) def dependencies_equal?(dependencies, other_dependencies) with_no_such_dependency_error_handling do specification_provider.dependencies_equal?(dependencies, other_dependencies) end end # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for) def name_for(dependency) with_no_such_dependency_error_handling do specification_provider.name_for(dependency) end end # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_explicit_dependency_source) def name_for_explicit_dependency_source with_no_such_dependency_error_handling do specification_provider.name_for_explicit_dependency_source end end # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_locking_dependency_source) def name_for_locking_dependency_source with_no_such_dependency_error_handling do specification_provider.name_for_locking_dependency_source end end # (see Gem::Resolver::Molinillo::SpecificationProvider#sort_dependencies) def sort_dependencies(dependencies, activated, conflicts) with_no_such_dependency_error_handling do specification_provider.sort_dependencies(dependencies, activated, conflicts) end end # (see Gem::Resolver::Molinillo::SpecificationProvider#allow_missing?) def allow_missing?(dependency) with_no_such_dependency_error_handling do specification_provider.allow_missing?(dependency) end end private # Ensures any raised {NoSuchDependencyError} has its # {NoSuchDependencyError#required_by} set. # @yield def with_no_such_dependency_error_handling yield rescue NoSuchDependencyError => error if state vertex = activated.vertex_named(name_for(error.dependency)) error.required_by += vertex.incoming_edges.map { |e| e.origin.name } error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty? end raise end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb 0000644 00000003653 15102661023 0024531 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo # @!visibility private module Delegates # Delegates all {Gem::Resolver::Molinillo::ResolutionState} methods to a `#state` property. module ResolutionState # (see Gem::Resolver::Molinillo::ResolutionState#name) def name current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.name end # (see Gem::Resolver::Molinillo::ResolutionState#requirements) def requirements current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.requirements end # (see Gem::Resolver::Molinillo::ResolutionState#activated) def activated current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.activated end # (see Gem::Resolver::Molinillo::ResolutionState#requirement) def requirement current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.requirement end # (see Gem::Resolver::Molinillo::ResolutionState#possibilities) def possibilities current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.possibilities end # (see Gem::Resolver::Molinillo::ResolutionState#depth) def depth current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.depth end # (see Gem::Resolver::Molinillo::ResolutionState#conflicts) def conflicts current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.conflicts end # (see Gem::Resolver::Molinillo::ResolutionState#unused_unwind_options) def unused_unwind_options current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty current_state.unused_unwind_options end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb 0000644 00000000213 15102661023 0021566 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo # The version of Gem::Resolver::Molinillo. VERSION = '0.7.0'.freeze end rubygems/rubygems/resolver/molinillo/lib/molinillo/resolver.rb 0000644 00000003045 15102661023 0021025 0 ustar 00 # frozen_string_literal: true require_relative 'dependency_graph' module Gem::Resolver::Molinillo # This class encapsulates a dependency resolver. # The resolver is responsible for determining which set of dependencies to # activate, with feedback from the {#specification_provider} # # class Resolver require_relative 'resolution' # @return [SpecificationProvider] the specification provider used # in the resolution process attr_reader :specification_provider # @return [UI] the UI module used to communicate back to the user # during the resolution process attr_reader :resolver_ui # Initializes a new resolver. # @param [SpecificationProvider] specification_provider # see {#specification_provider} # @param [UI] resolver_ui # see {#resolver_ui} def initialize(specification_provider, resolver_ui) @specification_provider = specification_provider @resolver_ui = resolver_ui end # Resolves the requested dependencies into a {DependencyGraph}, # locking to the base dependency graph (if specified) # @param [Array] requested an array of 'requested' dependencies that the # {#specification_provider} can understand # @param [DependencyGraph,nil] base the base dependency graph to which # dependencies should be 'locked' def resolve(requested, base = DependencyGraph.new) Resolution.new(specification_provider, resolver_ui, requested, base). resolve end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb 0000644 00000003321 15102661023 0021246 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo # Conveys information about the resolution process to a user. module UI # The {IO} object that should be used to print output. `STDOUT`, by default. # # @return [IO] def output STDOUT end # Called roughly every {#progress_rate}, this method should convey progress # to the user. # # @return [void] def indicate_progress output.print '.' unless debug? end # How often progress should be conveyed to the user via # {#indicate_progress}, in seconds. A third of a second, by default. # # @return [Float] def progress_rate 0.33 end # Called before resolution begins. # # @return [void] def before_resolution output.print 'Resolving dependencies...' end # Called after resolution ends (either successfully or with an error). # By default, prints a newline. # # @return [void] def after_resolution output.puts end # Conveys debug information to the user. # # @param [Integer] depth the current depth of the resolution process. # @return [void] def debug(depth = 0) if debug? debug_info = yield debug_info = debug_info.inspect unless debug_info.is_a?(String) debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" } output.puts debug_info end end # Whether or not debug messages should be printed. # By default, whether or not the `MOLINILLO_DEBUG` environment variable is # set. # # @return [Boolean] def debug? return @debug_mode if defined?(@debug_mode) @debug_mode = ENV['MOLINILLO_DEBUG'] end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb 0000644 00000010147 15102661023 0025367 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo # Provides information about specifications and dependencies to the resolver, # allowing the {Resolver} class to remain generic while still providing power # and flexibility. # # This module contains the methods that users of Gem::Resolver::Molinillo must to implement, # using knowledge of their own model classes. module SpecificationProvider # Search for the specifications that match the given dependency. # The specifications in the returned array will be considered in reverse # order, so the latest version ought to be last. # @note This method should be 'pure', i.e. the return value should depend # only on the `dependency` parameter. # # @param [Object] dependency # @return [Array<Object>] the specifications that satisfy the given # `dependency`. def search_for(dependency) [] end # Returns the dependencies of `specification`. # @note This method should be 'pure', i.e. the return value should depend # only on the `specification` parameter. # # @param [Object] specification # @return [Array<Object>] the dependencies that are required by the given # `specification`. def dependencies_for(specification) [] end # Determines whether the given `requirement` is satisfied by the given # `spec`, in the context of the current `activated` dependency graph. # # @param [Object] requirement # @param [DependencyGraph] activated the current dependency graph in the # resolution process. # @param [Object] spec # @return [Boolean] whether `requirement` is satisfied by `spec` in the # context of the current `activated` dependency graph. def requirement_satisfied_by?(requirement, activated, spec) true end # Determines whether two arrays of dependencies are equal, and thus can be # grouped. # # @param [Array<Object>] dependencies # @param [Array<Object>] other_dependencies # @return [Boolean] whether `dependencies` and `other_dependencies` should # be considered equal. def dependencies_equal?(dependencies, other_dependencies) dependencies == other_dependencies end # Returns the name for the given `dependency`. # @note This method should be 'pure', i.e. the return value should depend # only on the `dependency` parameter. # # @param [Object] dependency # @return [String] the name for the given `dependency`. def name_for(dependency) dependency.to_s end # @return [String] the name of the source of explicit dependencies, i.e. # those passed to {Resolver#resolve} directly. def name_for_explicit_dependency_source 'user-specified dependency' end # @return [String] the name of the source of 'locked' dependencies, i.e. # those passed to {Resolver#resolve} directly as the `base` def name_for_locking_dependency_source 'Lockfile' end # Sort dependencies so that the ones that are easiest to resolve are first. # Easiest to resolve is (usually) defined by: # 1) Is this dependency already activated? # 2) How relaxed are the requirements? # 3) Are there any conflicts for this dependency? # 4) How many possibilities are there to satisfy this dependency? # # @param [Array<Object>] dependencies # @param [DependencyGraph] activated the current dependency graph in the # resolution process. # @param [{String => Array<Conflict>}] conflicts # @return [Array<Object>] a sorted copy of `dependencies`. def sort_dependencies(dependencies, activated, conflicts) dependencies.sort_by do |dependency| name = name_for(dependency) [ activated.vertex_named(name).payload ? 0 : 1, conflicts[name] ? 0 : 1, ] end end # Returns whether this dependency, which has no possible matching # specifications, can safely be ignored. # # @param [Object] dependency # @return [Boolean] whether this dependency can safely be skipped. def allow_missing?(dependency) false end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb 0000644 00000002135 15102661023 0024766 0 ustar 00 # frozen_string_literal: true require_relative 'action' module Gem::Resolver::Molinillo class DependencyGraph # @!visibility private # @see DependencyGraph#set_payload class SetPayload < Action # :nodoc: # @!group Action # (see Action.action_name) def self.action_name :set_payload end # (see Action#up) def up(graph) vertex = graph.vertex_named(name) @old_payload = vertex.payload vertex.payload = payload end # (see Action#down) def down(graph) graph.vertex_named(name).payload = @old_payload end # @!group SetPayload # @return [String] the name of the vertex attr_reader :name # @return [Object] the payload for the vertex attr_reader :payload # Initialize an action to add set the payload for a vertex in a dependency # graph # @param [String] name the name of the vertex # @param [Object] payload the payload for the vertex def initialize(name, payload) @name = name @payload = payload end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb 0000644 00000001642 15102661023 0023741 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo class DependencyGraph # An action that modifies a {DependencyGraph} that is reversible. # @abstract class Action # rubocop:disable Lint/UnusedMethodArgument # @return [Symbol] The name of the action. def self.action_name raise 'Abstract' end # Performs the action on the given graph. # @param [DependencyGraph] graph the graph to perform the action on. # @return [Void] def up(graph) raise 'Abstract' end # Reverses the action on the given graph. # @param [DependencyGraph] graph the graph to reverse the action on. # @return [Void] def down(graph) raise 'Abstract' end # @return [Action,Nil] The previous action attr_accessor :previous # @return [Action,Nil] The next action attr_accessor :next end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb 0000644 00000012116 15102661023 0023777 0 ustar 00 # frozen_string_literal: true module Gem::Resolver::Molinillo class DependencyGraph # A vertex in a {DependencyGraph} that encapsulates a {#name} and a # {#payload} class Vertex # @return [String] the name of the vertex attr_accessor :name # @return [Object] the payload the vertex holds attr_accessor :payload # @return [Array<Object>] the explicit requirements that required # this vertex attr_reader :explicit_requirements # @return [Boolean] whether the vertex is considered a root vertex attr_accessor :root alias root? root # Initializes a vertex with the given name and payload. # @param [String] name see {#name} # @param [Object] payload see {#payload} def initialize(name, payload) @name = name.frozen? ? name : name.dup.freeze @payload = payload @explicit_requirements = [] @outgoing_edges = [] @incoming_edges = [] end # @return [Array<Object>] all of the requirements that required # this vertex def requirements (incoming_edges.map(&:requirement) + explicit_requirements).uniq end # @return [Array<Edge>] the edges of {#graph} that have `self` as their # {Edge#origin} attr_accessor :outgoing_edges # @return [Array<Edge>] the edges of {#graph} that have `self` as their # {Edge#destination} attr_accessor :incoming_edges # @return [Array<Vertex>] the vertices of {#graph} that have an edge with # `self` as their {Edge#destination} def predecessors incoming_edges.map(&:origin) end # @return [Set<Vertex>] the vertices of {#graph} where `self` is a # {#descendent?} def recursive_predecessors _recursive_predecessors end # @param [Set<Vertex>] vertices the set to add the predecessors to # @return [Set<Vertex>] the vertices of {#graph} where `self` is a # {#descendent?} def _recursive_predecessors(vertices = new_vertex_set) incoming_edges.each do |edge| vertex = edge.origin next unless vertices.add?(vertex) vertex._recursive_predecessors(vertices) end vertices end protected :_recursive_predecessors # @return [Array<Vertex>] the vertices of {#graph} that have an edge with # `self` as their {Edge#origin} def successors outgoing_edges.map(&:destination) end # @return [Set<Vertex>] the vertices of {#graph} where `self` is an # {#ancestor?} def recursive_successors _recursive_successors end # @param [Set<Vertex>] vertices the set to add the successors to # @return [Set<Vertex>] the vertices of {#graph} where `self` is an # {#ancestor?} def _recursive_successors(vertices = new_vertex_set) outgoing_edges.each do |edge| vertex = edge.destination next unless vertices.add?(vertex) vertex._recursive_successors(vertices) end vertices end protected :_recursive_successors # @return [String] a string suitable for debugging def inspect "#{self.class}:#{name}(#{payload.inspect})" end # @return [Boolean] whether the two vertices are equal, determined # by a recursive traversal of each {Vertex#successors} def ==(other) return true if equal?(other) shallow_eql?(other) && successors.to_set == other.successors.to_set end # @param [Vertex] other the other vertex to compare to # @return [Boolean] whether the two vertices are equal, determined # solely by {#name} and {#payload} equality def shallow_eql?(other) return true if equal?(other) other && name == other.name && payload == other.payload end alias eql? == # @return [Fixnum] a hash for the vertex based upon its {#name} def hash name.hash end # Is there a path from `self` to `other` following edges in the # dependency graph? # @return whether there is a path following edges within this {#graph} def path_to?(other) _path_to?(other) end alias descendent? path_to? # @param [Vertex] other the vertex to check if there's a path to # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited # @return [Boolean] whether there is a path to `other` from `self` def _path_to?(other, visited = new_vertex_set) return false unless visited.add?(self) return true if equal?(other) successors.any? { |v| v._path_to?(other, visited) } end protected :_path_to? # Is there a path from `other` to `self` following edges in the # dependency graph? # @return whether there is a path following edges within this {#graph} def ancestor?(other) other.path_to?(self) end alias is_reachable_from? ancestor? def new_vertex_set require 'set' Set.new end private :new_vertex_set end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb 0000644 00000003151 15102661023 0024606 0 ustar 00 # frozen_string_literal: true require_relative 'action' module Gem::Resolver::Molinillo class DependencyGraph # @!visibility private # (see DependencyGraph#add_vertex) class AddVertex < Action # :nodoc: # @!group Action # (see Action.action_name) def self.action_name :add_vertex end # (see Action#up) def up(graph) if existing = graph.vertices[name] @existing_payload = existing.payload @existing_root = existing.root end vertex = existing || Vertex.new(name, payload) graph.vertices[vertex.name] = vertex vertex.payload ||= payload vertex.root ||= root vertex end # (see Action#down) def down(graph) if defined?(@existing_payload) vertex = graph.vertices[name] vertex.payload = @existing_payload vertex.root = @existing_root else graph.vertices.delete(name) end end # @!group AddVertex # @return [String] the name of the vertex attr_reader :name # @return [Object] the payload for the vertex attr_reader :payload # @return [Boolean] whether the vertex is root or not attr_reader :root # Initialize an action to add a vertex to a dependency graph # @param [String] name the name of the vertex # @param [Object] payload the payload for the vertex # @param [Boolean] root whether the vertex is root or not def initialize(name, payload, root) @name = name @payload = payload @root = root end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb 0000644 00000001251 15102661023 0023233 0 ustar 00 # frozen_string_literal: true require_relative 'action' module Gem::Resolver::Molinillo class DependencyGraph # @!visibility private # @see DependencyGraph#tag class Tag < Action # @!group Action # (see Action.action_name) def self.action_name :tag end # (see Action#up) def up(graph) end # (see Action#down) def down(graph) end # @!group Tag # @return [Object] An opaque tag attr_reader :tag # Initialize an action to tag a state of a dependency graph # @param [Object] tag an opaque tag def initialize(tag) @tag = tag end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb 0000644 00000003450 15102661023 0024711 0 ustar 00 # frozen_string_literal: true require_relative 'action' module Gem::Resolver::Molinillo class DependencyGraph # @!visibility private # (see DependencyGraph#delete_edge) class DeleteEdge < Action # @!group Action # (see Action.action_name) def self.action_name :delete_edge end # (see Action#up) def up(graph) edge = make_edge(graph) edge.origin.outgoing_edges.delete(edge) edge.destination.incoming_edges.delete(edge) end # (see Action#down) def down(graph) edge = make_edge(graph) edge.origin.outgoing_edges << edge edge.destination.incoming_edges << edge edge end # @!group DeleteEdge # @return [String] the name of the origin of the edge attr_reader :origin_name # @return [String] the name of the destination of the edge attr_reader :destination_name # @return [Object] the requirement that the edge represents attr_reader :requirement # @param [DependencyGraph] graph the graph to find vertices from # @return [Edge] The edge this action adds def make_edge(graph) Edge.new( graph.vertex_named(origin_name), graph.vertex_named(destination_name), requirement ) end # Initialize an action to add an edge to a dependency graph # @param [String] origin_name the name of the origin of the edge # @param [String] destination_name the name of the destination of the edge # @param [Object] requirement the requirement that the edge represents def initialize(origin_name, destination_name, requirement) @origin_name = origin_name @destination_name = destination_name @requirement = requirement end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb 0000644 00000007045 15102661023 0023250 0 ustar 00 # frozen_string_literal: true require_relative 'add_edge_no_circular' require_relative 'add_vertex' require_relative 'delete_edge' require_relative 'detach_vertex_named' require_relative 'set_payload' require_relative 'tag' module Gem::Resolver::Molinillo class DependencyGraph # A log for dependency graph actions class Log # Initializes an empty log def initialize @current_action = @first_action = nil end # @!macro [new] action # {include:DependencyGraph#$0} # @param [Graph] graph the graph to perform the action on # @param (see DependencyGraph#$0) # @return (see DependencyGraph#$0) # @macro action def tag(graph, tag) push_action(graph, Tag.new(tag)) end # @macro action def add_vertex(graph, name, payload, root) push_action(graph, AddVertex.new(name, payload, root)) end # @macro action def detach_vertex_named(graph, name) push_action(graph, DetachVertexNamed.new(name)) end # @macro action def add_edge_no_circular(graph, origin, destination, requirement) push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement)) end # {include:DependencyGraph#delete_edge} # @param [Graph] graph the graph to perform the action on # @param [String] origin_name # @param [String] destination_name # @param [Object] requirement # @return (see DependencyGraph#delete_edge) def delete_edge(graph, origin_name, destination_name, requirement) push_action(graph, DeleteEdge.new(origin_name, destination_name, requirement)) end # @macro action def set_payload(graph, name, payload) push_action(graph, SetPayload.new(name, payload)) end # Pops the most recent action from the log and undoes the action # @param [DependencyGraph] graph # @return [Action] the action that was popped off the log def pop!(graph) return unless action = @current_action unless @current_action = action.previous @first_action = nil end action.down(graph) action end extend Enumerable # @!visibility private # Enumerates each action in the log # @yield [Action] def each return enum_for unless block_given? action = @first_action loop do break unless action yield action action = action.next end self end # @!visibility private # Enumerates each action in the log in reverse order # @yield [Action] def reverse_each return enum_for(:reverse_each) unless block_given? action = @current_action loop do break unless action yield action action = action.previous end self end # @macro action def rewind_to(graph, tag) loop do action = pop!(graph) raise "No tag #{tag.inspect} found" unless action break if action.class.action_name == :tag && action.tag == tag end end private # Adds the given action to the log, running the action # @param [DependencyGraph] graph # @param [Action] action # @return The value returned by `action.up` def push_action(graph, action) action.previous = @current_action @current_action.next = action if @current_action @current_action = action @first_action ||= action action.up(graph) end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb 0000644 00000003004 15102661023 0026447 0 ustar 00 # frozen_string_literal: true require_relative 'action' module Gem::Resolver::Molinillo class DependencyGraph # @!visibility private # @see DependencyGraph#detach_vertex_named class DetachVertexNamed < Action # @!group Action # (see Action#name) def self.action_name :add_vertex end # (see Action#up) def up(graph) return [] unless @vertex = graph.vertices.delete(name) removed_vertices = [@vertex] @vertex.outgoing_edges.each do |e| v = e.destination v.incoming_edges.delete(e) if !v.root? && v.incoming_edges.empty? removed_vertices.concat graph.detach_vertex_named(v.name) end end @vertex.incoming_edges.each do |e| v = e.origin v.outgoing_edges.delete(e) end removed_vertices end # (see Action#down) def down(graph) return unless @vertex graph.vertices[@vertex.name] = @vertex @vertex.outgoing_edges.each do |e| e.destination.incoming_edges << e end @vertex.incoming_edges.each do |e| e.origin.outgoing_edges << e end end # @!group DetachVertexNamed # @return [String] the name of the vertex to detach attr_reader :name # Initialize an action to detach a vertex from a dependency graph # @param [String] name the name of the vertex to detach def initialize(name) @name = name end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb 0000644 00000003565 15102661023 0026566 0 ustar 00 # frozen_string_literal: true require_relative 'action' module Gem::Resolver::Molinillo class DependencyGraph # @!visibility private # (see DependencyGraph#add_edge_no_circular) class AddEdgeNoCircular < Action # @!group Action # (see Action.action_name) def self.action_name :add_vertex end # (see Action#up) def up(graph) edge = make_edge(graph) edge.origin.outgoing_edges << edge edge.destination.incoming_edges << edge edge end # (see Action#down) def down(graph) edge = make_edge(graph) delete_first(edge.origin.outgoing_edges, edge) delete_first(edge.destination.incoming_edges, edge) end # @!group AddEdgeNoCircular # @return [String] the name of the origin of the edge attr_reader :origin # @return [String] the name of the destination of the edge attr_reader :destination # @return [Object] the requirement that the edge represents attr_reader :requirement # @param [DependencyGraph] graph the graph to find vertices from # @return [Edge] The edge this action adds def make_edge(graph) Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement) end # Initialize an action to add an edge to a dependency graph # @param [String] origin the name of the origin of the edge # @param [String] destination the name of the destination of the edge # @param [Object] requirement the requirement that the edge represents def initialize(origin, destination, requirement) @origin = origin @destination = destination @requirement = requirement end private def delete_first(array, item) return unless index = array.index(item) array.delete_at(index) end end end end rubygems/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb 0000644 00000020245 15102661023 0022464 0 ustar 00 # frozen_string_literal: true require_relative '../../../../tsort' require_relative 'dependency_graph/log' require_relative 'dependency_graph/vertex' module Gem::Resolver::Molinillo # A directed acyclic graph that is tuned to hold named dependencies class DependencyGraph include Enumerable # Enumerates through the vertices of the graph. # @return [Array<Vertex>] The graph's vertices. def each return vertices.values.each unless block_given? vertices.values.each { |v| yield v } end include Gem::TSort # @!visibility private alias tsort_each_node each # @!visibility private def tsort_each_child(vertex, &block) vertex.successors.each(&block) end # Topologically sorts the given vertices. # @param [Enumerable<Vertex>] vertices the vertices to be sorted, which must # all belong to the same graph. # @return [Array<Vertex>] The sorted vertices. def self.tsort(vertices) TSort.tsort( lambda { |b| vertices.each(&b) }, lambda { |v, &b| (v.successors & vertices).each(&b) } ) end # A directed edge of a {DependencyGraph} # @attr [Vertex] origin The origin of the directed edge # @attr [Vertex] destination The destination of the directed edge # @attr [Object] requirement The requirement the directed edge represents Edge = Struct.new(:origin, :destination, :requirement) # @return [{String => Vertex}] the vertices of the dependency graph, keyed # by {Vertex#name} attr_reader :vertices # @return [Log] the op log for this graph attr_reader :log # Initializes an empty dependency graph def initialize @vertices = {} @log = Log.new end # Tags the current state of the dependency as the given tag # @param [Object] tag an opaque tag for the current state of the graph # @return [Void] def tag(tag) log.tag(self, tag) end # Rewinds the graph to the state tagged as `tag` # @param [Object] tag the tag to rewind to # @return [Void] def rewind_to(tag) log.rewind_to(self, tag) end # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices} # are properly copied. # @param [DependencyGraph] other the graph to copy. def initialize_copy(other) super @vertices = {} @log = other.log.dup traverse = lambda do |new_v, old_v| return if new_v.outgoing_edges.size == old_v.outgoing_edges.size old_v.outgoing_edges.each do |edge| destination = add_vertex(edge.destination.name, edge.destination.payload) add_edge_no_circular(new_v, destination, edge.requirement) traverse.call(destination, edge.destination) end end other.vertices.each do |name, vertex| new_vertex = add_vertex(name, vertex.payload, vertex.root?) new_vertex.explicit_requirements.replace(vertex.explicit_requirements) traverse.call(new_vertex, vertex) end end # @return [String] a string suitable for debugging def inspect "#{self.class}:#{vertices.values.inspect}" end # @param [Hash] options options for dot output. # @return [String] Returns a dot format representation of the graph def to_dot(options = {}) edge_label = options.delete(:edge_label) raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty? dot_vertices = [] dot_edges = [] vertices.each do |n, v| dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]" v.outgoing_edges.each do |e| label = edge_label ? edge_label.call(e) : e.requirement dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]" end end dot_vertices.uniq! dot_vertices.sort! dot_edges.uniq! dot_edges.sort! dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}') dot.join("\n") end # @param [DependencyGraph] other # @return [Boolean] whether the two dependency graphs are equal, determined # by a recursive traversal of each {#root_vertices} and its # {Vertex#successors} def ==(other) return false unless other return true if equal?(other) vertices.each do |name, vertex| other_vertex = other.vertex_named(name) return false unless other_vertex return false unless vertex.payload == other_vertex.payload return false unless other_vertex.successors.to_set == vertex.successors.to_set end end # @param [String] name # @param [Object] payload # @param [Array<String>] parent_names # @param [Object] requirement the requirement that is requiring the child # @return [void] def add_child_vertex(name, payload, parent_names, requirement) root = !parent_names.delete(nil) { true } vertex = add_vertex(name, payload, root) vertex.explicit_requirements << requirement if root parent_names.each do |parent_name| parent_vertex = vertex_named(parent_name) add_edge(parent_vertex, vertex, requirement) end vertex end # Adds a vertex with the given name, or updates the existing one. # @param [String] name # @param [Object] payload # @return [Vertex] the vertex that was added to `self` def add_vertex(name, payload, root = false) log.add_vertex(self, name, payload, root) end # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively # removing any non-root vertices that were orphaned in the process # @param [String] name # @return [Array<Vertex>] the vertices which have been detached def detach_vertex_named(name) log.detach_vertex_named(self, name) end # @param [String] name # @return [Vertex,nil] the vertex with the given name def vertex_named(name) vertices[name] end # @param [String] name # @return [Vertex,nil] the root vertex with the given name def root_vertex_named(name) vertex = vertex_named(name) vertex if vertex && vertex.root? end # Adds a new {Edge} to the dependency graph # @param [Vertex] origin # @param [Vertex] destination # @param [Object] requirement the requirement that this edge represents # @return [Edge] the added edge def add_edge(origin, destination, requirement) if destination.path_to?(origin) raise CircularDependencyError.new(path(destination, origin)) end add_edge_no_circular(origin, destination, requirement) end # Deletes an {Edge} from the dependency graph # @param [Edge] edge # @return [Void] def delete_edge(edge) log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement) end # Sets the payload of the vertex with the given name # @param [String] name the name of the vertex # @param [Object] payload the payload # @return [Void] def set_payload(name, payload) log.set_payload(self, name, payload) end private # Adds a new {Edge} to the dependency graph without checking for # circularity. # @param (see #add_edge) # @return (see #add_edge) def add_edge_no_circular(origin, destination, requirement) log.add_edge_no_circular(self, origin.name, destination.name, requirement) end # Returns the path between two vertices # @raise [ArgumentError] if there is no path between the vertices # @param [Vertex] from # @param [Vertex] to # @return [Array<Vertex>] the shortest path from `from` to `to` def path(from, to) distances = Hash.new(vertices.size + 1) distances[from.name] = 0 predecessors = {} each do |vertex| vertex.successors.each do |successor| if distances[successor.name] > distances[vertex.name] + 1 distances[successor.name] = distances[vertex.name] + 1 predecessors[successor] = vertex end end end path = [to] while before = predecessors[to] path << before to = before break if to == from end unless path.last.equal?(from) raise ArgumentError, "There is no path from #{from.name} to #{to.name}" end path.reverse end end end rubygems/rubygems/resolver/molinillo/lib/molinillo.rb 0000644 00000000545 15102661023 0017166 0 ustar 00 # frozen_string_literal: true require_relative 'molinillo/gem_metadata' require_relative 'molinillo/errors' require_relative 'molinillo/resolver' require_relative 'molinillo/modules/ui' require_relative 'molinillo/modules/specification_provider' # Gem::Resolver::Molinillo is a generic dependency resolution algorithm. module Gem::Resolver::Molinillo end rubygems/rubygems/resolver/installed_specification.rb 0000644 00000002334 15102661023 0017301 0 ustar 00 # frozen_string_literal: true ## # An InstalledSpecification represents a gem that is already installed # locally. class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification def ==(other) # :nodoc: self.class === other and @set == other.set and @spec == other.spec end ## # This is a null install as this specification is already installed. # +options+ are ignored. def install(options = {}) yield nil end ## # Returns +true+ if this gem is installable for the current platform. def installable_platform? # BACKCOMPAT If the file is coming out of a specified file, then we # ignore the platform. This code can be removed in RG 3.0. return true if @source.kind_of? Gem::Source::SpecificFile super end def pretty_print(q) # :nodoc: q.group 2, '[InstalledSpecification', ']' do q.breakable q.text "name: #{name}" q.breakable q.text "version: #{version}" q.breakable q.text "platform: #{platform}" q.breakable q.text 'dependencies:' q.breakable q.pp spec.dependencies end end ## # The source for this specification def source @source ||= Gem::Source::Installed.new end end rubygems/rubygems/resolver/requirement_list.rb 0000644 00000002527 15102661023 0016021 0 ustar 00 # frozen_string_literal: true ## # The RequirementList is used to hold the requirements being considered # while resolving a set of gems. # # The RequirementList acts like a queue where the oldest items are removed # first. class Gem::Resolver::RequirementList include Enumerable ## # Creates a new RequirementList. def initialize @exact = [] @list = [] end def initialize_copy(other) # :nodoc: @exact = @exact.dup @list = @list.dup end ## # Adds Resolver::DependencyRequest +req+ to this requirements list. def add(req) if req.requirement.exact? @exact.push req else @list.push req end req end ## # Enumerates requirements in the list def each # :nodoc: return enum_for __method__ unless block_given? @exact.each do |requirement| yield requirement end @list.each do |requirement| yield requirement end end ## # How many elements are in the list def size @exact.size + @list.size end ## # Is the list empty? def empty? @exact.empty? && @list.empty? end ## # Remove the oldest DependencyRequest from the list. def remove return @exact.shift unless @exact.empty? @list.shift end ## # Returns the oldest five entries from the list. def next5 x = @exact[0,5] x + @list[0,5 - x.size] end end rubygems/rubygems/resolver/git_set.rb 0000644 00000005610 15102661023 0014060 0 ustar 00 # frozen_string_literal: true ## # A GitSet represents gems that are sourced from git repositories. # # This is used for gem dependency file support. # # Example: # # set = Gem::Resolver::GitSet.new # set.add_git_gem 'rake', 'git://example/rake.git', tag: 'rake-10.1.0' class Gem::Resolver::GitSet < Gem::Resolver::Set ## # The root directory for git gems in this set. This is usually Gem.dir, the # installation directory for regular gems. attr_accessor :root_dir ## # Contains repositories needing submodules attr_reader :need_submodules # :nodoc: ## # A Hash containing git gem names for keys and a Hash of repository and # git commit reference as values. attr_reader :repositories # :nodoc: ## # A hash of gem names to Gem::Resolver::GitSpecifications attr_reader :specs # :nodoc: def initialize # :nodoc: super() @git = ENV['git'] || 'git' @need_submodules = {} @repositories = {} @root_dir = Gem.dir @specs = {} end def add_git_gem(name, repository, reference, submodules) # :nodoc: @repositories[name] = [repository, reference] @need_submodules[repository] = submodules end ## # Adds and returns a GitSpecification with the given +name+ and +version+ # which came from a +repository+ at the given +reference+. If +submodules+ # is true they are checked out along with the repository. # # This fills in the prefetch information as enough information about the gem # is present in the arguments. def add_git_spec(name, version, repository, reference, submodules) # :nodoc: add_git_gem name, repository, reference, submodules source = Gem::Source::Git.new name, repository, reference source.root_dir = @root_dir spec = Gem::Specification.new do |s| s.name = name s.version = version end git_spec = Gem::Resolver::GitSpecification.new self, spec, source @specs[spec.name] = git_spec git_spec end ## # Finds all git gems matching +req+ def find_all(req) prefetch nil specs.values.select do |spec| req.match? spec end end ## # Prefetches specifications from the git repositories in this set. def prefetch(reqs) return unless @specs.empty? @repositories.each do |name, (repository, reference)| source = Gem::Source::Git.new name, repository, reference source.root_dir = @root_dir source.remote = @remote source.specs.each do |spec| git_spec = Gem::Resolver::GitSpecification.new self, spec, source @specs[spec.name] = git_spec end end end def pretty_print(q) # :nodoc: q.group 2, '[GitSet', ']' do next if @repositories.empty? q.breakable repos = @repositories.map do |name, (repository, reference)| "#{name}: #{repository}@#{reference}" end q.seplist repos do |repo| q.text repo end end end end rubygems/rubygems/resolver/lock_specification.rb 0000644 00000003502 15102661023 0016250 0 ustar 00 # frozen_string_literal: true ## # The LockSpecification comes from a lockfile (Gem::RequestSet::Lockfile). # # A LockSpecification's dependency information is pre-filled from the # lockfile. class Gem::Resolver::LockSpecification < Gem::Resolver::Specification attr_reader :sources def initialize(set, name, version, sources, platform) super() @name = name @platform = platform @set = set @source = sources.first @sources = sources @version = version @dependencies = [] @spec = nil end ## # This is a null install as a locked specification is considered installed. # +options+ are ignored. def install(options = {}) destination = options[:install_dir] || Gem.dir if File.exist? File.join(destination, 'specifications', spec.spec_name) yield nil return end super end ## # Adds +dependency+ from the lockfile to this specification def add_dependency(dependency) # :nodoc: @dependencies << dependency end def pretty_print(q) # :nodoc: q.group 2, '[LockSpecification', ']' do q.breakable q.text "name: #{@name}" q.breakable q.text "version: #{@version}" unless @platform == Gem::Platform::RUBY q.breakable q.text "platform: #{@platform}" end unless @dependencies.empty? q.breakable q.text 'dependencies:' q.breakable q.pp @dependencies end end end ## # A specification constructed from the lockfile is returned def spec @spec ||= Gem::Specification.find do |spec| spec.name == @name and spec.version == @version end @spec ||= Gem::Specification.new do |s| s.name = @name s.version = @version s.platform = @platform s.dependencies.concat @dependencies end end end rubygems/rubygems/resolver/set.rb 0000644 00000002344 15102661023 0013216 0 ustar 00 # frozen_string_literal: true ## # Resolver sets are used to look up specifications (and their # dependencies) used in resolution. This set is abstract. class Gem::Resolver::Set ## # Set to true to disable network access for this set attr_accessor :remote ## # Errors encountered when resolving gems attr_accessor :errors ## # When true, allows matching of requests to prerelease gems. attr_accessor :prerelease def initialize # :nodoc: @prerelease = false @remote = true @errors = [] end ## # The find_all method must be implemented. It returns all Resolver # Specification objects matching the given DependencyRequest +req+. def find_all(req) raise NotImplementedError end ## # The #prefetch method may be overridden, but this is not necessary. This # default implementation does nothing, which is suitable for sets where # looking up a specification is cheap (such as installed gems). # # When overridden, the #prefetch method should look up specifications # matching +reqs+. def prefetch(reqs) end ## # When true, this set is allowed to access the network when looking up # specifications or dependencies. def remote? # :nodoc: @remote end end rubygems/rubygems/resolver/conflict.rb 0000644 00000006377 15102661023 0014236 0 ustar 00 # frozen_string_literal: true ## # Used internally to indicate that a dependency conflicted # with a spec that would be activated. class Gem::Resolver::Conflict ## # The specification that was activated prior to the conflict attr_reader :activated ## # The dependency that is in conflict with the activated gem. attr_reader :dependency attr_reader :failed_dep # :nodoc: ## # Creates a new resolver conflict when +dependency+ is in conflict with an # already +activated+ specification. def initialize(dependency, activated, failed_dep=dependency) @dependency = dependency @activated = activated @failed_dep = failed_dep end def ==(other) # :nodoc: self.class === other and @dependency == other.dependency and @activated == other.activated and @failed_dep == other.failed_dep end ## # A string explanation of the conflict. def explain "<Conflict wanted: #{@failed_dep}, had: #{activated.spec.full_name}>" end ## # Return the 2 dependency objects that conflicted def conflicting_dependencies [@failed_dep.dependency, @activated.request.dependency] end ## # Explanation of the conflict used by exceptions to print useful messages def explanation activated = @activated.spec.full_name dependency = @failed_dep.dependency requirement = dependency.requirement alternates = dependency.matching_specs.map {|spec| spec.full_name } unless alternates.empty? matching = <<-MATCHING.chomp Gems matching %s: %s MATCHING matching = matching % [ dependency, alternates.join(', '), ] end explanation = <<-EXPLANATION Activated %s which does not match conflicting dependency (%s) Conflicting dependency chains: %s versus: %s %s EXPLANATION explanation % [ activated, requirement, request_path(@activated).reverse.join(", depends on\n "), request_path(@failed_dep).reverse.join(", depends on\n "), matching ] end ## # Returns true if the conflicting dependency's name matches +spec+. def for_spec?(spec) @dependency.name == spec.name end def pretty_print(q) # :nodoc: q.group 2, '[Dependency conflict: ', ']' do q.breakable q.text 'activated ' q.pp @activated q.breakable q.text ' dependency ' q.pp @dependency q.breakable if @dependency == @failed_dep q.text ' failed' else q.text ' failed dependency ' q.pp @failed_dep end end end ## # Path of activations from the +current+ list. def request_path(current) path = [] while current do case current when Gem::Resolver::ActivationRequest then path << "#{current.request.dependency}, #{current.spec.version} activated" current = current.parent when Gem::Resolver::DependencyRequest then path << "#{current.dependency}" current = current.requester else raise Gem::Exception, "[BUG] unknown request class #{current.class}" end end path = ['user request (gem command or Gemfile)'] if path.empty? path end ## # Return the Specification that listed the dependency def requester @failed_dep.requester end end rubygems/rubygems/resolver/api_set.rb 0000644 00000005531 15102661023 0014050 0 ustar 00 # frozen_string_literal: true ## # The global rubygems pool, available via the rubygems.org API. # Returns instances of APISpecification. class Gem::Resolver::APISet < Gem::Resolver::Set autoload :GemParser, File.expand_path("api_set/gem_parser", __dir__) ## # The URI for the dependency API this APISet uses. attr_reader :dep_uri # :nodoc: ## # The Gem::Source that gems are fetched from attr_reader :source ## # The corresponding place to fetch gems. attr_reader :uri ## # Creates a new APISet that will retrieve gems from +uri+ using the RubyGems # API URL +dep_uri+ which is described at # https://guides.rubygems.org/rubygems-org-api def initialize(dep_uri = 'https://index.rubygems.org/info/') super() dep_uri = URI dep_uri unless URI === dep_uri @dep_uri = dep_uri @uri = dep_uri + '..' @data = Hash.new {|h,k| h[k] = [] } @source = Gem::Source.new @uri @to_fetch = [] end ## # Return an array of APISpecification objects matching # DependencyRequest +req+. def find_all(req) res = [] return res unless @remote if @to_fetch.include?(req.name) prefetch_now end versions(req.name).each do |ver| if req.dependency.match? req.name, ver[:number], @prerelease res << Gem::Resolver::APISpecification.new(self, ver) end end res end ## # A hint run by the resolver to allow the Set to fetch # data for DependencyRequests +reqs+. def prefetch(reqs) return unless @remote names = reqs.map {|r| r.dependency.name } needed = names - @data.keys - @to_fetch @to_fetch += needed end def prefetch_now # :nodoc: needed, @to_fetch = @to_fetch, [] needed.sort.each do |name| versions(name) end end def pretty_print(q) # :nodoc: q.group 2, '[APISet', ']' do q.breakable q.text "URI: #{@dep_uri}" q.breakable q.text 'gem names:' q.pp @data.keys end end ## # Return data for all versions of the gem +name+. def versions(name) # :nodoc: if @data.key?(name) return @data[name] end uri = @dep_uri + name str = Gem::RemoteFetcher.fetcher.fetch_path uri lines(str).each do |ver| number, platform, dependencies, requirements = parse_gem(ver) platform ||= "ruby" dependencies = dependencies.map {|dep_name, reqs| [dep_name, reqs.join(", ")] } requirements = requirements.map {|req_name, reqs| [req_name.to_sym, reqs] }.to_h @data[name] << { name: name, number: number, platform: platform, dependencies: dependencies, requirements: requirements } end @data[name] end private def lines(str) lines = str.split("\n") header = lines.index("---") header ? lines[header + 1..-1] : lines end def parse_gem(string) @gem_parser ||= GemParser.new @gem_parser.parse(string) end end rubygems/rubygems/resolver/installer_set.rb 0000644 00000014740 15102661023 0015276 0 ustar 00 # frozen_string_literal: true ## # A set of gems for installation sourced from remote sources and local .gem # files class Gem::Resolver::InstallerSet < Gem::Resolver::Set ## # List of Gem::Specification objects that must always be installed. attr_reader :always_install # :nodoc: ## # Only install gems in the always_install list attr_accessor :ignore_dependencies # :nodoc: ## # Do not look in the installed set when finding specifications. This is # used by the --install-dir option to `gem install` attr_accessor :ignore_installed # :nodoc: ## # The remote_set looks up remote gems for installation. attr_reader :remote_set # :nodoc: ## # Ignore ruby & rubygems specification constraints. # attr_accessor :force # :nodoc: ## # Creates a new InstallerSet that will look for gems in +domain+. def initialize(domain) super() @domain = domain @f = Gem::SpecFetcher.fetcher @always_install = [] @ignore_dependencies = false @ignore_installed = false @local = {} @local_source = Gem::Source::Local.new @remote_set = Gem::Resolver::BestSet.new @force = false @specs = {} end ## # Looks up the latest specification for +dependency+ and adds it to the # always_install list. def add_always_install(dependency) request = Gem::Resolver::DependencyRequest.new dependency, nil found = find_all request found.delete_if do |s| s.version.prerelease? and not s.local? end unless dependency.prerelease? found = found.select do |s| Gem::Source::SpecificFile === s.source or Gem::Platform::RUBY == s.platform or Gem::Platform.local === s.platform end found = found.sort_by do |s| [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1] end newest = found.last unless @force found_matching_metadata = found.reverse.find do |spec| metadata_satisfied?(spec) end if found_matching_metadata.nil? if newest ensure_required_ruby_version_met(newest.spec) ensure_required_rubygems_version_met(newest.spec) else exc = Gem::UnsatisfiableDependencyError.new request exc.errors = errors raise exc end else newest = found_matching_metadata end end @always_install << newest.spec end ## # Adds a local gem requested using +dep_name+ with the given +spec+ that can # be loaded and installed using the +source+. def add_local(dep_name, spec, source) @local[dep_name] = [spec, source] end ## # Should local gems should be considered? def consider_local? # :nodoc: @domain == :both or @domain == :local end ## # Should remote gems should be considered? def consider_remote? # :nodoc: @domain == :both or @domain == :remote end ## # Errors encountered while resolving gems def errors @errors + @remote_set.errors end ## # Returns an array of IndexSpecification objects matching DependencyRequest # +req+. def find_all(req) res = [] dep = req.dependency return res if @ignore_dependencies and @always_install.none? {|spec| dep.match? spec } name = dep.name dep.matching_specs.each do |gemspec| next if @always_install.any? {|spec| spec.name == gemspec.name } res << Gem::Resolver::InstalledSpecification.new(self, gemspec) end unless @ignore_installed if consider_local? matching_local = @local.values.select do |spec, _| req.match? spec end.map do |spec, source| Gem::Resolver::LocalSpecification.new self, spec, source end res.concat matching_local begin if local_spec = @local_source.find_gem(name, dep.requirement) res << Gem::Resolver::IndexSpecification.new( self, local_spec.name, local_spec.version, @local_source, local_spec.platform) end rescue Gem::Package::FormatError # ignore end end res.delete_if do |spec| spec.version.prerelease? and not dep.prerelease? end res.concat @remote_set.find_all req if consider_remote? res end def prefetch(reqs) @remote_set.prefetch(reqs) if consider_remote? end def prerelease=(allow_prerelease) super @remote_set.prerelease = allow_prerelease end def inspect # :nodoc: always_install = @always_install.map {|s| s.full_name } '#<%s domain: %s specs: %p always install: %p>' % [ self.class, @domain, @specs.keys, always_install ] end ## # Called from IndexSpecification to get a true Specification # object. def load_spec(name, ver, platform, source) # :nodoc: key = "#{name}-#{ver}-#{platform}" @specs.fetch key do tuple = Gem::NameTuple.new name, ver, platform @specs[key] = source.fetch_spec tuple end end ## # Has a local gem for +dep_name+ been added to this set? def local?(dep_name) # :nodoc: spec, _ = @local[dep_name] spec end def pretty_print(q) # :nodoc: q.group 2, '[InstallerSet', ']' do q.breakable q.text "domain: #{@domain}" q.breakable q.text 'specs: ' q.pp @specs.keys q.breakable q.text 'always install: ' q.pp @always_install end end def remote=(remote) # :nodoc: case @domain when :local then @domain = :both if remote when :remote then @domain = nil unless remote when :both then @domain = :local unless remote end end private def metadata_satisfied?(spec) spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version) end def ensure_required_ruby_version_met(spec) # :nodoc: if rrv = spec.required_ruby_version ruby_version = Gem.ruby_version unless rrv.satisfied_by? ruby_version raise Gem::RuntimeRequirementNotMetError, "#{spec.full_name} requires Ruby version #{rrv}. The current ruby version is #{ruby_version}." end end end def ensure_required_rubygems_version_met(spec) # :nodoc: if rrgv = spec.required_rubygems_version unless rrgv.satisfied_by? Gem.rubygems_version rg_version = Gem::VERSION raise Gem::RuntimeRequirementNotMetError, "#{spec.full_name} requires RubyGems version #{rrgv}. The current RubyGems version is #{rg_version}. " + "Try 'gem update --system' to update RubyGems itself." end end end end rubygems/rubygems/resolver/spec_specification.rb 0000644 00000002525 15102661023 0016256 0 ustar 00 # frozen_string_literal: true ## # The Resolver::SpecSpecification contains common functionality for # Resolver specifications that are backed by a Gem::Specification. class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification ## # A SpecSpecification is created for a +set+ for a Gem::Specification in # +spec+. The +source+ is either where the +spec+ came from, or should be # loaded from. def initialize(set, spec, source = nil) @set = set @source = source @spec = spec end ## # The dependencies of the gem for this specification def dependencies spec.dependencies end ## # The required_ruby_version constraint for this specification def required_ruby_version spec.required_ruby_version end ## # The required_rubygems_version constraint for this specification def required_rubygems_version spec.required_rubygems_version end ## # The name and version of the specification. # # Unlike Gem::Specification#full_name, the platform is not included. def full_name "#{spec.name}-#{spec.version}" end ## # The name of the gem for this specification def name spec.name end ## # The platform this gem works on. def platform spec.platform end ## # The version of the gem for this specification. def version spec.version end end rubygems/rubygems/resolver/local_specification.rb 0000644 00000001445 15102661023 0016416 0 ustar 00 # frozen_string_literal: true ## # A LocalSpecification comes from a .gem file on the local filesystem. class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification ## # Returns +true+ if this gem is installable for the current platform. def installable_platform? return true if @source.kind_of? Gem::Source::SpecificFile super end def local? # :nodoc: true end def pretty_print(q) # :nodoc: q.group 2, '[LocalSpecification', ']' do q.breakable q.text "name: #{name}" q.breakable q.text "version: #{version}" q.breakable q.text "platform: #{platform}" q.breakable q.text 'dependencies:' q.breakable q.pp dependencies q.breakable q.text "source: #{@source.path}" end end end rubygems/rubygems/resolver/best_set.rb 0000644 00000003173 15102661023 0014234 0 ustar 00 # frozen_string_literal: true ## # The BestSet chooses the best available method to query a remote index. # # It combines IndexSet and APISet class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet ## # Creates a BestSet for the given +sources+ or Gem::sources if none are # specified. +sources+ must be a Gem::SourceList. def initialize(sources = Gem.sources) super() @sources = sources end ## # Picks which sets to use for the configured sources. def pick_sets # :nodoc: @sources.each_source do |source| @sets << source.dependency_resolver_set end end def find_all(req) # :nodoc: pick_sets if @remote and @sets.empty? super rescue Gem::RemoteFetcher::FetchError => e replace_failed_api_set e retry end def prefetch(reqs) # :nodoc: pick_sets if @remote and @sets.empty? super end def pretty_print(q) # :nodoc: q.group 2, '[BestSet', ']' do q.breakable q.text 'sets:' q.breakable q.pp @sets end end ## # Replaces a failed APISet for the URI in +error+ with an IndexSet. # # If no matching APISet can be found the original +error+ is raised. # # The calling method must retry the exception to repeat the lookup. def replace_failed_api_set(error) # :nodoc: uri = error.original_uri uri = URI uri unless URI === uri uri = uri + "." raise error unless api_set = @sets.find do |set| Gem::Resolver::APISet === set and set.dep_uri == uri end index_set = Gem::Resolver::IndexSet.new api_set.source @sets.map! do |set| next set unless set == api_set index_set end end end rubygems/rubygems/resolver/activation_request.rb 0000644 00000005630 15102661023 0016335 0 ustar 00 # frozen_string_literal: true ## # Specifies a Specification object that should be activated. Also contains a # dependency that was used to introduce this activation. class Gem::Resolver::ActivationRequest ## # The parent request for this activation request. attr_reader :request ## # The specification to be activated. attr_reader :spec ## # Creates a new ActivationRequest that will activate +spec+. The parent # +request+ is used to provide diagnostics in case of conflicts. def initialize(spec, request) @spec = spec @request = request end def ==(other) # :nodoc: case other when Gem::Specification @spec == other when Gem::Resolver::ActivationRequest @spec == other.spec else false end end def eql?(other) self == other end def hash @spec.hash end ## # Is this activation request for a development dependency? def development? @request.development? end ## # Downloads a gem at +path+ and returns the file path. def download(path) Gem.ensure_gem_subdirectories path if @spec.respond_to? :sources exception = nil path = @spec.sources.find do |source| begin source.download full_spec, path rescue exception end end return path if path raise exception if exception elsif @spec.respond_to? :source source = @spec.source source.download full_spec, path else source = Gem.sources.first source.download full_spec, path end end ## # The full name of the specification to be activated. def full_name name_tuple.full_name end alias_method :to_s, :full_name ## # The Gem::Specification for this activation request. def full_spec Gem::Specification === @spec ? @spec : @spec.spec end def inspect # :nodoc: '#<%s for %p from %s>' % [ self.class, @spec, @request ] end ## # True if the requested gem has already been installed. def installed? case @spec when Gem::Resolver::VendorSpecification then true else this_spec = full_spec Gem::Specification.any? do |s| s == this_spec end end end ## # The name of this activation request's specification def name @spec.name end ## # Return the ActivationRequest that contained the dependency # that we were activated for. def parent @request.requester end def pretty_print(q) # :nodoc: q.group 2, '[Activation request', ']' do q.breakable q.pp @spec q.breakable q.text ' for ' q.pp @request end end ## # The version of this activation request's specification def version @spec.version end ## # The platform of this activation request's specification def platform @spec.platform end private def name_tuple @name_tuple ||= Gem::NameTuple.new(name, version, platform) end end rubygems/rubygems/resolver/molinillo.rb 0000644 00000000111 15102661023 0014407 0 ustar 00 # frozen_string_literal: true require_relative 'molinillo/lib/molinillo' rubygems/rubygems/resolver/vendor_specification.rb 0000644 00000001103 15102661023 0016610 0 ustar 00 # frozen_string_literal: true ## # A VendorSpecification represents a gem that has been unpacked into a project # and is being loaded through a gem dependencies file through the +path:+ # option. class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification def ==(other) # :nodoc: self.class === other and @set == other.set and @spec == other.spec and @source == other.source end ## # This is a null install as this gem was unpacked into a directory. # +options+ are ignored. def install(options = {}) yield nil end end rubygems/rubygems/resolver/git_specification.rb 0000644 00000002425 15102661023 0016106 0 ustar 00 # frozen_string_literal: true ## # A GitSpecification represents a gem that is sourced from a git repository # and is being loaded through a gem dependencies file through the +git:+ # option. class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification def ==(other) # :nodoc: self.class === other and @set == other.set and @spec == other.spec and @source == other.source end def add_dependency(dependency) # :nodoc: spec.dependencies << dependency end ## # Installing a git gem only involves building the extensions and generating # the executables. def install(options = {}) require_relative '../installer' installer = Gem::Installer.for_spec spec, options yield installer if block_given? installer.run_pre_install_hooks installer.build_extensions installer.run_post_build_hooks installer.generate_bin installer.run_post_install_hooks end def pretty_print(q) # :nodoc: q.group 2, '[GitSpecification', ']' do q.breakable q.text "name: #{name}" q.breakable q.text "version: #{version}" q.breakable q.text 'dependencies:' q.breakable q.pp dependencies q.breakable q.text "source:" q.breakable q.pp @source end end end rubygems/rubygems/resolver/index_specification.rb 0000644 00000004456 15102661023 0016440 0 ustar 00 # frozen_string_literal: true ## # Represents a possible Specification object returned from IndexSet. Used to # delay needed to download full Specification objects when only the +name+ # and +version+ are needed. class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification ## # An IndexSpecification is created from the index format described in `gem # help generate_index`. # # The +set+ contains other specifications for this (URL) +source+. # # The +name+, +version+ and +platform+ are the name, version and platform of # the gem. def initialize(set, name, version, source, platform) super() @set = set @name = name @version = version @source = source @platform = platform.to_s @spec = nil end ## # The dependencies of the gem for this specification def dependencies spec.dependencies end ## # The required_ruby_version constraint for this specification # # A fallback is included because when generated, some marshalled specs have it # set to +nil+. def required_ruby_version spec.required_ruby_version || Gem::Requirement.default end ## # The required_rubygems_version constraint for this specification # # A fallback is included because the original version of the specification # API didn't include that field, so some marshalled specs in the index have it # set to +nil+. def required_rubygems_version spec.required_rubygems_version || Gem::Requirement.default end def ==(other) self.class === other && @name == other.name && @version == other.version && @platform == other.platform end def hash @name.hash ^ @version.hash ^ @platform.hash end def inspect # :nodoc: '#<%s %s source %s>' % [self.class, full_name, @source] end def pretty_print(q) # :nodoc: q.group 2, '[Index specification', ']' do q.breakable q.text full_name unless Gem::Platform::RUBY == @platform q.breakable q.text @platform.to_s end q.breakable q.text 'source ' q.pp @source end end ## # Fetches a Gem::Specification for this IndexSpecification from the #source. def spec # :nodoc: @spec ||= begin tuple = Gem::NameTuple.new @name, @version, @platform @source.fetch_spec tuple end end end rubygems/rubygems/resolver/api_set/gem_parser.rb 0000644 00000001250 15102661023 0016166 0 ustar 00 # frozen_string_literal: true class Gem::Resolver::APISet::GemParser def parse(line) version_and_platform, rest = line.split(" ", 2) version, platform = version_and_platform.split("-", 2) dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] requirements = requirements ? requirements.map {|d| parse_dependency(d) } : [] [version, platform, dependencies, requirements] end private def parse_dependency(string) dependency = string.split(":") dependency[-1] = dependency[-1].split("&") if dependency.size > 1 dependency end end rubygems/rubygems/resolver/current_set.rb 0000644 00000000430 15102661023 0014752 0 ustar 00 # frozen_string_literal: true ## # A set which represents the installed gems. Respects # all the normal settings that control where to look # for installed gems. class Gem::Resolver::CurrentSet < Gem::Resolver::Set def find_all(req) req.dependency.matching_specs end end rubygems/rubygems/optparse.rb 0000644 00000000110 15102661023 0012404 0 ustar 00 # frozen_string_literal: true require_relative 'optparse/lib/optparse' rubygems/rubygems/util.rb 0000644 00000005000 15102661023 0011527 0 ustar 00 # frozen_string_literal: true require_relative 'deprecate' ## # This module contains various utility methods as module methods. module Gem::Util ## # Zlib::GzipReader wrapper that unzips +data+. def self.gunzip(data) require 'zlib' require 'stringio' data = StringIO.new(data, 'r') gzip_reader = begin Zlib::GzipReader.new(data) rescue Zlib::GzipFile::Error => e raise e.class, e.inspect, e.backtrace end unzipped = gzip_reader.read unzipped.force_encoding Encoding::BINARY unzipped end ## # Zlib::GzipWriter wrapper that zips +data+. def self.gzip(data) require 'zlib' require 'stringio' zipped = StringIO.new(String.new, 'w') zipped.set_encoding Encoding::BINARY Zlib::GzipWriter.wrap zipped do |io| io.write data end zipped.string end ## # A Zlib::Inflate#inflate wrapper def self.inflate(data) require 'zlib' Zlib::Inflate.inflate data end ## # This calls IO.popen and reads the result def self.popen(*command) IO.popen command, &:read end ## # Invokes system, but silences all output. def self.silent_system(*command) opt = {:out => IO::NULL, :err => [:child, :out]} if Hash === command.last opt.update(command.last) cmds = command[0...-1] else cmds = command.dup end system(*(cmds << opt)) end class << self extend Gem::Deprecate rubygems_deprecate :silent_system end ## # Enumerates the parents of +directory+. def self.traverse_parents(directory, &block) return enum_for __method__, directory unless block_given? here = File.expand_path directory loop do Dir.chdir here, &block rescue Errno::EACCES new_here = File.expand_path('..', here) return if new_here == here # toplevel here = new_here end end ## # Globs for files matching +pattern+ inside of +directory+, # returning absolute paths to the matching files. def self.glob_files_in_dir(glob, base_path) if RUBY_VERSION >= "2.5" Dir.glob(glob, base: base_path).map! {|f| File.expand_path(f, base_path) } else Dir.glob(File.expand_path(glob, base_path)) end end ## # Corrects +path+ (usually returned by `URI.parse().path` on Windows), that # comes with a leading slash. def self.correct_for_windows_path(path) if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':' path[1..-1] else path end end end rubygems/rubygems/indexer.rb 0000644 00000025546 15102661023 0012231 0 ustar 00 # frozen_string_literal: true require_relative '../rubygems' require_relative 'package' require 'tmpdir' ## # Top level class for building the gem repository index. class Gem::Indexer include Gem::UserInteraction ## # Build indexes for RubyGems 1.2.0 and newer when true attr_accessor :build_modern ## # Index install location attr_reader :dest_directory ## # Specs index install location attr_reader :dest_specs_index ## # Latest specs index install location attr_reader :dest_latest_specs_index ## # Prerelease specs index install location attr_reader :dest_prerelease_specs_index ## # Index build directory attr_reader :directory ## # Create an indexer that will index the gems in +directory+. def initialize(directory, options = {}) require 'fileutils' require 'tmpdir' require 'zlib' options = { :build_modern => true }.merge options @build_modern = options[:build_modern] @dest_directory = directory @directory = Dir.mktmpdir 'gem_generate_index' marshal_name = "Marshal.#{Gem.marshal_version}" @master_index = File.join @directory, 'yaml' @marshal_index = File.join @directory, marshal_name @quick_dir = File.join @directory, 'quick' @quick_marshal_dir = File.join @quick_dir, marshal_name @quick_marshal_dir_base = File.join "quick", marshal_name # FIX: UGH @quick_index = File.join @quick_dir, 'index' @latest_index = File.join @quick_dir, 'latest_index' @specs_index = File.join @directory, "specs.#{Gem.marshal_version}" @latest_specs_index = File.join(@directory, "latest_specs.#{Gem.marshal_version}") @prerelease_specs_index = File.join(@directory, "prerelease_specs.#{Gem.marshal_version}") @dest_specs_index = File.join(@dest_directory, "specs.#{Gem.marshal_version}") @dest_latest_specs_index = File.join(@dest_directory, "latest_specs.#{Gem.marshal_version}") @dest_prerelease_specs_index = File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}") @files = [] end ## # Build various indices def build_indices specs = map_gems_to_specs gem_file_list Gem::Specification._resort! specs build_marshal_gemspecs specs build_modern_indices specs if @build_modern compress_indices end ## # Builds Marshal quick index gemspecs. def build_marshal_gemspecs(specs) count = specs.count progress = ui.progress_reporter count, "Generating Marshal quick index gemspecs for #{count} gems", "Complete" files = [] Gem.time 'Generated Marshal quick index gemspecs' do specs.each do |spec| next if spec.default_gem? spec_file_name = "#{spec.original_name}.gemspec.rz" marshal_name = File.join @quick_marshal_dir, spec_file_name marshal_zipped = Gem.deflate Marshal.dump(spec) File.open marshal_name, 'wb' do |io| io.write marshal_zipped end files << marshal_name progress.updated spec.original_name end progress.done end @files << @quick_marshal_dir files end ## # Build a single index for RubyGems 1.2 and newer def build_modern_index(index, file, name) say "Generating #{name} index" Gem.time "Generated #{name} index" do File.open(file, 'wb') do |io| specs = index.map do |*spec| # We have to splat here because latest_specs is an array, while the # others are hashes. spec = spec.flatten.last platform = spec.original_platform # win32-api-1.0.4-x86-mswin32-60 unless String === platform alert_warning "Skipping invalid platform in gem: #{spec.full_name}" next end platform = Gem::Platform::RUBY if platform.nil? or platform.empty? [spec.name, spec.version, platform] end specs = compact_specs(specs) Marshal.dump(specs, io) end end end ## # Builds indices for RubyGems 1.2 and newer. Handles full, latest, prerelease def build_modern_indices(specs) prerelease, released = specs.partition do |s| s.version.prerelease? end latest_specs = Gem::Specification._latest_specs specs build_modern_index(released.sort, @specs_index, 'specs') build_modern_index(latest_specs.sort, @latest_specs_index, 'latest specs') build_modern_index(prerelease.sort, @prerelease_specs_index, 'prerelease specs') @files += [@specs_index, "#{@specs_index}.gz", @latest_specs_index, "#{@latest_specs_index}.gz", @prerelease_specs_index, "#{@prerelease_specs_index}.gz"] end def map_gems_to_specs(gems) gems.map do |gemfile| if File.size(gemfile) == 0 alert_warning "Skipping zero-length gem: #{gemfile}" next end begin spec = Gem::Package.new(gemfile).spec spec.loaded_from = gemfile spec.abbreviate spec.sanitize spec rescue SignalException alert_error "Received signal, exiting" raise rescue Exception => e msg = ["Unable to process #{gemfile}", "#{e.message} (#{e.class})", "\t#{e.backtrace.join "\n\t"}"].join("\n") alert_error msg end end.compact end ## # Compresses indices on disk #-- # All future files should be compressed using gzip, not deflate def compress_indices say "Compressing indices" Gem.time 'Compressed indices' do if @build_modern gzip @specs_index gzip @latest_specs_index gzip @prerelease_specs_index end end end ## # Compacts Marshal output for the specs index data source by using identical # objects as much as possible. def compact_specs(specs) names = {} versions = {} platforms = {} specs.map do |(name, version, platform)| names[name] = name unless names.include? name versions[version] = version unless versions.include? version platforms[platform] = platform unless platforms.include? platform [names[name], versions[version], platforms[platform]] end end ## # Compress +filename+ with +extension+. def compress(filename, extension) data = Gem.read_binary filename zipped = Gem.deflate data File.open "#{filename}.#{extension}", 'wb' do |io| io.write zipped end end ## # List of gem file names to index. def gem_file_list Gem::Util.glob_files_in_dir("*.gem", File.join(@dest_directory, "gems")) end ## # Builds and installs indices. def generate_index make_temp_directories build_indices install_indices rescue SignalException ensure FileUtils.rm_rf @directory end ## # Zlib::GzipWriter wrapper that gzips +filename+ on disk. def gzip(filename) Zlib::GzipWriter.open "#{filename}.gz" do |io| io.write Gem.read_binary(filename) end end ## # Install generated indices into the destination directory. def install_indices verbose = Gem.configuration.really_verbose say "Moving index into production dir #{@dest_directory}" if verbose files = @files files.delete @quick_marshal_dir if files.include? @quick_dir if files.include? @quick_marshal_dir and not files.include? @quick_dir files.delete @quick_marshal_dir dst_name = File.join(@dest_directory, @quick_marshal_dir_base) FileUtils.mkdir_p File.dirname(dst_name), :verbose => verbose FileUtils.rm_rf dst_name, :verbose => verbose FileUtils.mv(@quick_marshal_dir, dst_name, :verbose => verbose, :force => true) end files = files.map do |path| path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK? end files.each do |file| src_name = File.join @directory, file dst_name = File.join @dest_directory, file FileUtils.rm_rf dst_name, :verbose => verbose FileUtils.mv(src_name, @dest_directory, :verbose => verbose, :force => true) end end ## # Make directories for index generation def make_temp_directories FileUtils.rm_rf @directory FileUtils.mkdir_p @directory, :mode => 0700 FileUtils.mkdir_p @quick_marshal_dir end ## # Ensure +path+ and path with +extension+ are identical. def paranoid(path, extension) data = Gem.read_binary path compressed_data = Gem.read_binary "#{path}.#{extension}" unless data == Gem::Util.inflate(compressed_data) raise "Compressed file #{compressed_path} does not match uncompressed file #{path}" end end ## # Perform an in-place update of the repository from newly added gems. def update_index make_temp_directories specs_mtime = File.stat(@dest_specs_index).mtime newest_mtime = Time.at 0 updated_gems = gem_file_list.select do |gem| gem_mtime = File.stat(gem).mtime newest_mtime = gem_mtime if gem_mtime > newest_mtime gem_mtime >= specs_mtime end if updated_gems.empty? say 'No new gems' terminate_interaction 0 end specs = map_gems_to_specs updated_gems prerelease, released = specs.partition {|s| s.version.prerelease? } files = build_marshal_gemspecs specs Gem.time 'Updated indexes' do update_specs_index released, @dest_specs_index, @specs_index update_specs_index released, @dest_latest_specs_index, @latest_specs_index update_specs_index(prerelease, @dest_prerelease_specs_index, @prerelease_specs_index) end compress_indices verbose = Gem.configuration.really_verbose say "Updating production dir #{@dest_directory}" if verbose files << @specs_index files << "#{@specs_index}.gz" files << @latest_specs_index files << "#{@latest_specs_index}.gz" files << @prerelease_specs_index files << "#{@prerelease_specs_index}.gz" files = files.map do |path| path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK? end files.each do |file| src_name = File.join @directory, file dst_name = File.join @dest_directory, file # REFACTOR: duped above FileUtils.mv src_name, dst_name, :verbose => verbose, :force => true File.utime newest_mtime, newest_mtime, dst_name end end ## # Combines specs in +index+ and +source+ then writes out a new copy to # +dest+. For a latest index, does not ensure the new file is minimal. def update_specs_index(index, source, dest) specs_index = Marshal.load Gem.read_binary(source) index.each do |spec| platform = spec.original_platform platform = Gem::Platform::RUBY if platform.nil? or platform.empty? specs_index << [spec.name, spec.version, platform] end specs_index = compact_specs specs_index.uniq.sort File.open dest, 'wb' do |io| Marshal.dump specs_index, io end end end rubygems/rubygems/core_ext/tcpsocket_init.rb 0000644 00000002633 15102661023 0015415 0 ustar 00 require 'socket' module CoreExtensions module TCPSocketExt def self.prepended(base) base.prepend Initializer end module Initializer CONNECTION_TIMEOUT = 5 IPV4_DELAY_SECONDS = 0.1 def initialize(host, serv, *rest) mutex = Thread::Mutex.new addrs = [] threads = [] cond_var = Thread::ConditionVariable.new Addrinfo.foreach(host, serv, nil, :STREAM) do |addr| Thread.report_on_exception = false if defined? Thread.report_on_exception = () threads << Thread.new(addr) do # give head start to ipv6 addresses sleep IPV4_DELAY_SECONDS if addr.ipv4? # raises Errno::ECONNREFUSED when ip:port is unreachable Socket.tcp(addr.ip_address, serv, connect_timeout: CONNECTION_TIMEOUT).close mutex.synchronize do addrs << addr.ip_address cond_var.signal end end end mutex.synchronize do timeout_time = CONNECTION_TIMEOUT + Time.now.to_f while addrs.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0 cond_var.wait(mutex, remaining_time) end host = addrs.shift unless addrs.empty? end threads.each {|t| t.kill.join if t.alive? } super(host, serv, *rest) end end end end TCPSocket.prepend CoreExtensions::TCPSocketExt rubygems/rubygems/core_ext/kernel_gem.rb 0000644 00000004713 15102661023 0014504 0 ustar 00 # frozen_string_literal: true ## # RubyGems adds the #gem method to allow activation of specific gem versions # and overrides the #require method on Kernel to make gems appear as if they # live on the <code>$LOAD_PATH</code>. See the documentation of these methods # for further detail. module Kernel ## # Use Kernel#gem to activate a specific version of +gem_name+. # # +requirements+ is a list of version requirements that the # specified gem must match, most commonly "= example.version.number". See # Gem::Requirement for how to specify a version requirement. # # If you will be activating the latest version of a gem, there is no need to # call Kernel#gem, Kernel#require will do the right thing for you. # # Kernel#gem returns true if the gem was activated, otherwise false. If the # gem could not be found, didn't match the version requirements, or a # different version was already activated, an exception will be raised. # # Kernel#gem should be called *before* any require statements (otherwise # RubyGems may load a conflicting library version). # # Kernel#gem only loads prerelease versions when prerelease +requirements+ # are given: # # gem 'rake', '>= 1.1.a', '< 2' # # In older RubyGems versions, the environment variable GEM_SKIP could be # used to skip activation of specified gems, for example to test out changes # that haven't been installed yet. Now RubyGems defers to -I and the # RUBYLIB environment variable to skip activation of a gem. # # Example: # # GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb def gem(gem_name, *requirements) # :doc: skip_list = (ENV['GEM_SKIP'] || "").split(/:/) raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name if gem_name.kind_of? Gem::Dependency unless Gem::Deprecate.skip warn "#{Gem.location_of_caller.join ':'}:Warning: Kernel.gem no longer "\ "accepts a Gem::Dependency object, please pass the name "\ "and requirements directly" end requirements = gem_name.requirement gem_name = gem_name.name end dep = Gem::Dependency.new(gem_name, *requirements) loaded = Gem.loaded_specs[gem_name] return false if loaded && dep.matches_spec?(loaded) spec = dep.to_spec if spec if Gem::LOADED_SPECS_MUTEX.owned? spec.activate else Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate } end end end private :gem end rubygems/rubygems/core_ext/kernel_warn.rb 0000644 00000002501 15102661023 0014674 0 ustar 00 # frozen_string_literal: true # `uplevel` keyword argument of Kernel#warn is available since ruby 2.5. if RUBY_VERSION >= "2.5" && !Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES module Kernel rubygems_path = "#{__dir__}/" # Frames to be skipped start with this path. original_warn = instance_method(:warn) remove_method :warn class << self remove_method :warn end module_function define_method(:warn) {|*messages, **kw| unless uplevel = kw[:uplevel] if Gem.java_platform? return original_warn.bind(self).call(*messages) else return original_warn.bind(self).call(*messages, **kw) end end # Ensure `uplevel` fits a `long` uplevel, = [uplevel].pack("l!").unpack("l!") if uplevel >= 0 start = 0 while uplevel >= 0 loc, = caller_locations(start, 1) unless loc # No more backtrace start += uplevel break end start += 1 if path = loc.path unless path.start_with?(rubygems_path) or path.start_with?('<internal:') # Non-rubygems frames uplevel -= 1 end end end kw[:uplevel] = start end original_warn.bind(self).call(*messages, **kw) } end end rubygems/rubygems/core_ext/kernel_require.rb 0000644 00000012353 15102661023 0015407 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require 'monitor' module Kernel RUBYGEMS_ACTIVATION_MONITOR = Monitor.new # :nodoc: # Make sure we have a reference to Ruby's original Kernel#require unless defined?(gem_original_require) alias gem_original_require require private :gem_original_require end file = Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES ? "<internal:#{__FILE__}>" : __FILE__ module_eval <<'RUBY', file, __LINE__ + 1 ## # When RubyGems is required, Kernel#require is replaced with our own which # is capable of loading gems on demand. # # When you call <tt>require 'x'</tt>, this is what happens: # * If the file can be loaded from the existing Ruby loadpath, it # is. # * Otherwise, installed gems are searched for a file that matches. # If it's found in gem 'y', that gem is activated (added to the # loadpath). # # The normal <tt>require</tt> functionality of returning false if # that file has already been loaded is preserved. def require(path) if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?) monitor_owned = RUBYGEMS_ACTIVATION_MONITOR.mon_owned? end RUBYGEMS_ACTIVATION_MONITOR.enter path = path.to_path if path.respond_to? :to_path if spec = Gem.find_unresolved_default_spec(path) # Ensure -I beats a default gem resolved_path = begin rp = nil load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths Gem.suffixes.each do |s| $LOAD_PATH[0...load_path_check_index].each do |lp| safe_lp = lp.dup.tap(&Gem::UNTAINT) begin if File.symlink? safe_lp # for backward compatibility next end rescue SecurityError RUBYGEMS_ACTIVATION_MONITOR.exit raise end full_path = File.expand_path(File.join(safe_lp, "#{path}#{s}")) if File.file?(full_path) rp = full_path break end end break if rp end rp end begin Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease) rescue Exception RUBYGEMS_ACTIVATION_MONITOR.exit raise end unless resolved_path end # If there are no unresolved deps, then we can use just try # normal require handle loading a gem from the rescue below. if Gem::Specification.unresolved_deps.empty? RUBYGEMS_ACTIVATION_MONITOR.exit return gem_original_require(path) end # If +path+ is for a gem that has already been loaded, don't # bother trying to find it in an unresolved gem, just go straight # to normal require. #-- # TODO request access to the C implementation of this to speed up RubyGems if Gem::Specification.find_active_stub_by_path(path) RUBYGEMS_ACTIVATION_MONITOR.exit return gem_original_require(path) end # Attempt to find +path+ in any unresolved gems... found_specs = Gem::Specification.find_in_unresolved path # If there are no directly unresolved gems, then try and find +path+ # in any gems that are available via the currently unresolved gems. # For example, given: # # a => b => c => d # # If a and b are currently active with c being unresolved and d.rb is # requested, then find_in_unresolved_tree will find d.rb in d because # it's a dependency of c. # if found_specs.empty? found_specs = Gem::Specification.find_in_unresolved_tree path found_specs.each do |found_spec| found_spec.activate end # We found +path+ directly in an unresolved gem. Now we figure out, of # the possible found specs, which one we should activate. else # Check that all the found specs are just different # versions of the same gem names = found_specs.map(&:name).uniq if names.size > 1 RUBYGEMS_ACTIVATION_MONITOR.exit raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}" end # Ok, now find a gem that has no conflicts, starting # at the highest version. valid = found_specs.find {|s| !s.has_conflicts? } unless valid le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate" le.name = names.first RUBYGEMS_ACTIVATION_MONITOR.exit raise le end valid.activate end RUBYGEMS_ACTIVATION_MONITOR.exit return gem_original_require(path) rescue LoadError => load_error RUBYGEMS_ACTIVATION_MONITOR.enter begin if load_error.path == path and Gem.try_activate(path) require_again = true end ensure RUBYGEMS_ACTIVATION_MONITOR.exit end return gem_original_require(path) if require_again raise load_error ensure if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?) if monitor_owned != (ow = RUBYGEMS_ACTIVATION_MONITOR.mon_owned?) STDERR.puts [$$, Thread.current, $!, $!.backtrace].inspect if $! raise "CRITICAL: RUBYGEMS_ACTIVATION_MONITOR.owned?: before #{monitor_owned} -> after #{ow}" end end end RUBY private :require end rubygems/rubygems/source_list.rb 0000644 00000005022 15102661023 0013111 0 ustar 00 # frozen_string_literal: true ## # The SourceList represents the sources rubygems has been configured to use. # A source may be created from an array of sources: # # Gem::SourceList.from %w[https://rubygems.example https://internal.example] # # Or by adding them: # # sources = Gem::SourceList.new # sources << 'https://rubygems.example' # # The most common way to get a SourceList is Gem.sources. class Gem::SourceList include Enumerable ## # Creates a new SourceList def initialize @sources = [] end ## # The sources in this list attr_reader :sources ## # Creates a new SourceList from an array of sources. def self.from(ary) list = new list.replace ary return list end def initialize_copy(other) # :nodoc: @sources = @sources.dup end ## # Appends +obj+ to the source list which may be a Gem::Source, URI or URI # String. def <<(obj) require "uri" src = case obj when URI Gem::Source.new(obj) when Gem::Source obj else Gem::Source.new(URI.parse(obj)) end @sources << src unless @sources.include?(src) src end ## # Replaces this SourceList with the sources in +other+ See #<< for # acceptable items in +other+. def replace(other) clear other.each do |x| self << x end self end ## # Removes all sources from the SourceList. def clear @sources.clear end ## # Yields each source URI in the list. def each @sources.each {|s| yield s.uri.to_s } end ## # Yields each source in the list. def each_source(&b) @sources.each(&b) end ## # Returns true if there are no sources in this SourceList. def empty? @sources.empty? end def ==(other) # :nodoc: to_a == other end ## # Returns an Array of source URI Strings. def to_a @sources.map {|x| x.uri.to_s } end alias_method :to_ary, :to_a ## # Returns the first source in the list. def first @sources.first end ## # Returns true if this source list includes +other+ which may be a # Gem::Source or a source URI. def include?(other) if other.kind_of? Gem::Source @sources.include? other else @sources.find {|x| x.uri.to_s == other.to_s } end end ## # Deletes +source+ from the source list which may be a Gem::Source or a URI. def delete(source) if source.kind_of? Gem::Source @sources.delete source else @sources.delete_if {|x| x.uri.to_s == source.to_s } end end end rubygems/rubygems/user_interaction.rb 0000644 00000032165 15102661023 0014143 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require_relative 'deprecate' require_relative 'text' ## # Module that defines the default UserInteraction. Any class including this # module will have access to the +ui+ method that returns the default UI. module Gem::DefaultUserInteraction include Gem::Text ## # The default UI is a class variable of the singleton class for this # module. @ui = nil ## # Return the default UI. def self.ui @ui ||= Gem::ConsoleUI.new end ## # Set the default UI. If the default UI is never explicitly set, a simple # console based UserInteraction will be used automatically. def self.ui=(new_ui) @ui = new_ui end ## # Use +new_ui+ for the duration of +block+. def self.use_ui(new_ui) old_ui = @ui @ui = new_ui yield ensure @ui = old_ui end ## # See DefaultUserInteraction::ui def ui Gem::DefaultUserInteraction.ui end ## # See DefaultUserInteraction::ui= def ui=(new_ui) Gem::DefaultUserInteraction.ui = new_ui end ## # See DefaultUserInteraction::use_ui def use_ui(new_ui, &block) Gem::DefaultUserInteraction.use_ui(new_ui, &block) end end ## # UserInteraction allows RubyGems to interact with the user through standard # methods that can be replaced with more-specific UI methods for different # displays. # # Since UserInteraction dispatches to a concrete UI class you may need to # reference other classes for specific behavior such as Gem::ConsoleUI or # Gem::SilentUI. # # Example: # # class X # include Gem::UserInteraction # # def get_answer # n = ask("What is the meaning of life?") # end # end module Gem::UserInteraction include Gem::DefaultUserInteraction ## # Displays an alert +statement+. Asks a +question+ if given. def alert(statement, question = nil) ui.alert statement, question end ## # Displays an error +statement+ to the error output location. Asks a # +question+ if given. def alert_error(statement, question = nil) ui.alert_error statement, question end ## # Displays a warning +statement+ to the warning output location. Asks a # +question+ if given. def alert_warning(statement, question = nil) ui.alert_warning statement, question end ## # Asks a +question+ and returns the answer. def ask(question) ui.ask question end ## # Asks for a password with a +prompt+ def ask_for_password(prompt) ui.ask_for_password prompt end ## # Asks a yes or no +question+. Returns true for yes, false for no. def ask_yes_no(question, default = nil) ui.ask_yes_no question, default end ## # Asks the user to answer +question+ with an answer from the given +list+. def choose_from_list(question, list) ui.choose_from_list question, list end ## # Displays the given +statement+ on the standard output (or equivalent). def say(statement = '') ui.say statement end ## # Terminates the RubyGems process with the given +exit_code+ def terminate_interaction(exit_code = 0) ui.terminate_interaction exit_code end ## # Calls +say+ with +msg+ or the results of the block if really_verbose # is true. def verbose(msg = nil) say(clean_text(msg || yield)) if Gem.configuration.really_verbose end end ## # Gem::StreamUI implements a simple stream based user interface. class Gem::StreamUI extend Gem::Deprecate ## # The input stream attr_reader :ins ## # The output stream attr_reader :outs ## # The error stream attr_reader :errs ## # Creates a new StreamUI wrapping +in_stream+ for user input, +out_stream+ # for standard output, +err_stream+ for error output. If +usetty+ is true # then special operations (like asking for passwords) will use the TTY # commands to disable character echo. def initialize(in_stream, out_stream, err_stream=STDERR, usetty=true) @ins = in_stream @outs = out_stream @errs = err_stream @usetty = usetty end ## # Returns true if TTY methods should be used on this StreamUI. def tty? @usetty && @ins.tty? end ## # Prints a formatted backtrace to the errors stream if backtraces are # enabled. def backtrace(exception) return unless Gem.configuration.backtrace @errs.puts "\t#{exception.backtrace.join "\n\t"}" end ## # Choose from a list of options. +question+ is a prompt displayed above # the list. +list+ is a list of option strings. Returns the pair # [option_name, option_index]. def choose_from_list(question, list) @outs.puts question list.each_with_index do |item, index| @outs.puts " #{index + 1}. #{item}" end @outs.print "> " @outs.flush result = @ins.gets return nil, nil unless result result = result.strip.to_i - 1 return list[result], result end ## # Ask a question. Returns a true for yes, false for no. If not connected # to a tty, raises an exception if default is nil, otherwise returns # default. def ask_yes_no(question, default=nil) unless tty? if default.nil? raise Gem::OperationNotSupportedError, "Not connected to a tty and no default specified" else return default end end default_answer = case default when nil 'yn' when true 'Yn' else 'yN' end result = nil while result.nil? do result = case ask "#{question} [#{default_answer}]" when /^y/i then true when /^n/i then false when /^$/ then default else nil end end return result end ## # Ask a question. Returns an answer if connected to a tty, nil otherwise. def ask(question) return nil if not tty? @outs.print(question + " ") @outs.flush result = @ins.gets result.chomp! if result result end ## # Ask for a password. Does not echo response to terminal. def ask_for_password(question) return nil if not tty? @outs.print(question, " ") @outs.flush password = _gets_noecho @outs.puts password.chomp! if password password end def require_io_console @require_io_console ||= begin begin require 'io/console' rescue LoadError end true end end def _gets_noecho require_io_console @ins.noecho { @ins.gets } end ## # Display a statement. def say(statement="") @outs.puts statement end ## # Display an informational alert. Will ask +question+ if it is not nil. def alert(statement, question=nil) @outs.puts "INFO: #{statement}" ask(question) if question end ## # Display a warning on stderr. Will ask +question+ if it is not nil. def alert_warning(statement, question=nil) @errs.puts "WARNING: #{statement}" ask(question) if question end ## # Display an error message in a location expected to get error messages. # Will ask +question+ if it is not nil. def alert_error(statement, question=nil) @errs.puts "ERROR: #{statement}" ask(question) if question end ## # Terminate the application with exit code +status+, running any exit # handlers that might have been defined. def terminate_interaction(status = 0) close raise Gem::SystemExitException, status end def close end ## # Return a progress reporter object chosen from the current verbosity. def progress_reporter(*args) case Gem.configuration.verbose when nil, false SilentProgressReporter.new(@outs, *args) when true SimpleProgressReporter.new(@outs, *args) else VerboseProgressReporter.new(@outs, *args) end end ## # An absolutely silent progress reporter. class SilentProgressReporter ## # The count of items is never updated for the silent progress reporter. attr_reader :count ## # Creates a silent progress reporter that ignores all input arguments. def initialize(out_stream, size, initial_message, terminal_message = nil) end ## # Does not print +message+ when updated as this object has taken a vow of # silence. def updated(message) end ## # Does not print anything when complete as this object has taken a vow of # silence. def done end end ## # A basic dotted progress reporter. class SimpleProgressReporter include Gem::DefaultUserInteraction ## # The number of progress items counted so far. attr_reader :count ## # Creates a new progress reporter that will write to +out_stream+ for # +size+ items. Shows the given +initial_message+ when progress starts # and the +terminal_message+ when it is complete. def initialize(out_stream, size, initial_message, terminal_message = "complete") @out = out_stream @total = size @count = 0 @terminal_message = terminal_message @out.puts initial_message end ## # Prints out a dot and ignores +message+. def updated(message) @count += 1 @out.print "." @out.flush end ## # Prints out the terminal message. def done @out.puts "\n#{@terminal_message}" end end ## # A progress reporter that prints out messages about the current progress. class VerboseProgressReporter include Gem::DefaultUserInteraction ## # The number of progress items counted so far. attr_reader :count ## # Creates a new progress reporter that will write to +out_stream+ for # +size+ items. Shows the given +initial_message+ when progress starts # and the +terminal_message+ when it is complete. def initialize(out_stream, size, initial_message, terminal_message = 'complete') @out = out_stream @total = size @count = 0 @terminal_message = terminal_message @out.puts initial_message end ## # Prints out the position relative to the total and the +message+. def updated(message) @count += 1 @out.puts "#{@count}/#{@total}: #{message}" end ## # Prints out the terminal message. def done @out.puts @terminal_message end end ## # Return a download reporter object chosen from the current verbosity def download_reporter(*args) if [nil, false].include?(Gem.configuration.verbose) || !@outs.tty? SilentDownloadReporter.new(@outs, *args) else ThreadedDownloadReporter.new(@outs, *args) end end ## # An absolutely silent download reporter. class SilentDownloadReporter ## # The silent download reporter ignores all arguments def initialize(out_stream, *args) end ## # The silent download reporter does not display +filename+ or care about # +filesize+ because it is silent. def fetch(filename, filesize) end ## # Nothing can update the silent download reporter. def update(current) end ## # The silent download reporter won't tell you when the download is done. # Because it is silent. def done end end ## # A progress reporter that behaves nicely with threaded downloading. class ThreadedDownloadReporter MUTEX = Thread::Mutex.new ## # The current file name being displayed attr_reader :file_name ## # Creates a new threaded download reporter that will display on # +out_stream+. The other arguments are ignored. def initialize(out_stream, *args) @file_name = nil @out = out_stream end ## # Tells the download reporter that the +file_name+ is being fetched. # The other arguments are ignored. def fetch(file_name, *args) if @file_name.nil? @file_name = file_name locked_puts "Fetching #{@file_name}" end end ## # Updates the threaded download reporter for the given number of +bytes+. def update(bytes) # Do nothing. end ## # Indicates the download is complete. def done # Do nothing. end private def locked_puts(message) MUTEX.synchronize do @out.puts message end end end end ## # Subclass of StreamUI that instantiates the user interaction using STDIN, # STDOUT, and STDERR. class Gem::ConsoleUI < Gem::StreamUI ## # The Console UI has no arguments as it defaults to reading input from # stdin, output to stdout and warnings or errors to stderr. def initialize super STDIN, STDOUT, STDERR, true end end ## # SilentUI is a UI choice that is absolutely silent. class Gem::SilentUI < Gem::StreamUI ## # The SilentUI has no arguments as it does not use any stream. def initialize reader, writer = nil, nil reader = File.open(IO::NULL, 'r') writer = File.open(IO::NULL, 'w') super reader, writer, writer, false end def close super @ins.close @outs.close end def download_reporter(*args) # :nodoc: SilentDownloadReporter.new(@outs, *args) end def progress_reporter(*args) # :nodoc: SilentProgressReporter.new(@outs, *args) end end rubygems/rubygems/local_remote_options.rb 0000644 00000007077 15102661023 0015012 0 ustar 00 # frozen_string_literal: true #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require 'uri' require_relative '../rubygems' ## # Mixin methods for local and remote Gem::Command options. module Gem::LocalRemoteOptions ## # Allows Gem::OptionParser to handle HTTP URIs. def accept_uri_http Gem::OptionParser.accept URI::HTTP do |value| begin uri = URI.parse value rescue URI::InvalidURIError raise Gem::OptionParser::InvalidArgument, value end valid_uri_schemes = ["http", "https", "file", "s3"] unless valid_uri_schemes.include?(uri.scheme) msg = "Invalid uri scheme for #{value}\nPreface URLs with one of #{valid_uri_schemes.map{|s| "#{s}://" }}" raise ArgumentError, msg end value end end ## # Add local/remote options to the command line parser. def add_local_remote_options add_option(:"Local/Remote", '-l', '--local', 'Restrict operations to the LOCAL domain') do |value, options| options[:domain] = :local end add_option(:"Local/Remote", '-r', '--remote', 'Restrict operations to the REMOTE domain') do |value, options| options[:domain] = :remote end add_option(:"Local/Remote", '-b', '--both', 'Allow LOCAL and REMOTE operations') do |value, options| options[:domain] = :both end add_bulk_threshold_option add_clear_sources_option add_source_option add_proxy_option add_update_sources_option end ## # Add the --bulk-threshold option def add_bulk_threshold_option add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT', "Threshold for switching to bulk", "synchronization (default #{Gem.configuration.bulk_threshold})") do |value, options| Gem.configuration.bulk_threshold = value.to_i end end ## # Add the --clear-sources option def add_clear_sources_option add_option(:"Local/Remote", '--clear-sources', 'Clear the gem sources') do |value, options| Gem.sources = nil options[:sources_cleared] = true end end ## # Add the --http-proxy option def add_proxy_option accept_uri_http add_option(:"Local/Remote", '-p', '--[no-]http-proxy [URL]', URI::HTTP, 'Use HTTP proxy for remote operations') do |value, options| options[:http_proxy] = (value == false) ? :no_proxy : value Gem.configuration[:http_proxy] = options[:http_proxy] end end ## # Add the --source option def add_source_option accept_uri_http add_option(:"Local/Remote", '-s', '--source URL', URI::HTTP, 'Append URL to list of remote gem sources') do |source, options| source << '/' if source !~ /\/\z/ if options.delete :sources_cleared Gem.sources = [source] else Gem.sources << source unless Gem.sources.include?(source) end end end ## # Add the --update-sources option def add_update_sources_option add_option(:Deprecated, '-u', '--[no-]update-sources', 'Update local source cache') do |value, options| Gem.configuration.update_sources = value end end ## # Is fetching of local and remote information enabled? def both? options[:domain] == :both end ## # Is local fetching enabled? def local? options[:domain] == :local || options[:domain] == :both end ## # Is remote fetching enabled? def remote? options[:domain] == :remote || options[:domain] == :both end end rubygems/rubygems/mock_gem_ui.rb 0000644 00000002604 15102661023 0013037 0 ustar 00 # frozen_string_literal: true require_relative 'user_interaction' ## # This Gem::StreamUI subclass records input and output to StringIO for # retrieval during tests. class Gem::MockGemUi < Gem::StreamUI ## # Raised when you haven't provided enough input to your MockGemUi class InputEOFError < RuntimeError def initialize(question) super "Out of input for MockGemUi on #{question.inspect}" end end class TermError < RuntimeError attr_reader :exit_code def initialize(exit_code) super @exit_code = exit_code end end class SystemExitException < RuntimeError; end module TTY attr_accessor :tty def tty?() @tty = true unless defined?(@tty) @tty end def noecho yield self end end def initialize(input = "") require 'stringio' ins = StringIO.new input outs = StringIO.new errs = StringIO.new ins.extend TTY outs.extend TTY errs.extend TTY super ins, outs, errs, true @terminated = false end def ask(question) raise InputEOFError, question if @ins.eof? super end def input @ins.string end def output @outs.string end def error @errs.string end def terminated? @terminated end def terminate_interaction(status=0) @terminated = true raise TermError, status if status != 0 raise SystemExitException end end rubygems/rubygems/available_set.rb 0000644 00000006015 15102661023 0013354 0 ustar 00 # frozen_string_literal: true class Gem::AvailableSet include Enumerable Tuple = Struct.new(:spec, :source) attr_accessor :remote # :nodoc: def initialize @set = [] @sorted = nil @remote = true end attr_reader :set def add(spec, source) @set << Tuple.new(spec, source) @sorted = nil self end def <<(o) case o when Gem::AvailableSet s = o.set when Array s = o.map do |sp,so| if !sp.kind_of?(Gem::Specification) or !so.kind_of?(Gem::Source) raise TypeError, "Array must be in [[spec, source], ...] form" end Tuple.new(sp,so) end else raise TypeError, "must be a Gem::AvailableSet" end @set += s @sorted = nil self end ## # Yields each Tuple in this AvailableSet def each return enum_for __method__ unless block_given? @set.each do |tuple| yield tuple end end ## # Yields the Gem::Specification for each Tuple in this AvailableSet def each_spec return enum_for __method__ unless block_given? each do |tuple| yield tuple.spec end end def empty? @set.empty? end def all_specs @set.map {|t| t.spec } end def match_platform! @set.reject! {|t| !Gem::Platform.match_spec?(t.spec) } @sorted = nil self end def sorted @sorted ||= @set.sort do |a,b| i = b.spec <=> a.spec i != 0 ? i : (a.source <=> b.source) end end def size @set.size end def source_for(spec) f = @set.find {|t| t.spec == spec } f.source end ## # Converts this AvailableSet into a RequestSet that can be used to install # gems. # # If +development+ is :none then no development dependencies are installed. # Other options are :shallow for only direct development dependencies of the # gems in this set or :all for all development dependencies. def to_request_set(development = :none) request_set = Gem::RequestSet.new request_set.development = :all == development each_spec do |spec| request_set.always_install << spec request_set.gem spec.name, spec.version request_set.import spec.development_dependencies if :shallow == development end request_set end ## # # Used by the Resolver, the protocol to use a AvailableSet as a # search Set. def find_all(req) dep = req.dependency match = @set.find_all do |t| dep.match? t.spec end match.map do |t| Gem::Resolver::LocalSpecification.new(self, t.spec, t.source) end end def prefetch(reqs) end def pick_best! return self if empty? @set = [sorted.first] @sorted = nil self end def remove_installed!(dep) @set.reject! do |t| # already locally installed Gem::Specification.any? do |installed_spec| dep.name == installed_spec.name and dep.requirement.satisfied_by? installed_spec.version end end @sorted = nil self end def inject_into_list(dep_list) @set.each {|t| dep_list.add t.spec } end end rubygems/rubygems/uri_formatter.rb 0000644 00000001415 15102661023 0013442 0 ustar 00 # frozen_string_literal: true ## # The UriFormatter handles URIs from user-input and escaping. # # uf = Gem::UriFormatter.new 'example.com' # # p uf.normalize #=> 'http://example.com' class Gem::UriFormatter ## # The URI to be formatted. attr_reader :uri ## # Creates a new URI formatter for +uri+. def initialize(uri) require 'cgi' @uri = uri end ## # Escapes the #uri for use as a CGI parameter def escape return unless @uri CGI.escape @uri end ## # Normalize the URI by adding "http://" if it is missing. def normalize (@uri =~ /^(https?|ftp|file):/i) ? @uri : "http://#{@uri}" end ## # Unescapes the #uri which came from a CGI parameter def unescape return unless @uri CGI.unescape @uri end end rubygems/rubygems/basic_specification.rb 0000644 00000017320 15102661023 0014543 0 ustar 00 # frozen_string_literal: true ## # BasicSpecification is an abstract class which implements some common code # used by both Specification and StubSpecification. class Gem::BasicSpecification ## # Allows installation of extensions for git: gems. attr_writer :base_dir # :nodoc: ## # Sets the directory where extensions for this gem will be installed. attr_writer :extension_dir # :nodoc: ## # Is this specification ignored for activation purposes? attr_writer :ignored # :nodoc: ## # The path this gemspec was loaded from. This attribute is not persisted. attr_accessor :loaded_from ## # Allows correct activation of git: and path: gems. attr_writer :full_gem_path # :nodoc: def initialize internal_init end def self.default_specifications_dir Gem.default_specifications_dir end class << self extend Gem::Deprecate rubygems_deprecate :default_specifications_dir, "Gem.default_specifications_dir" end ## # The path to the gem.build_complete file within the extension install # directory. def gem_build_complete_path # :nodoc: File.join extension_dir, 'gem.build_complete' end ## # True when the gem has been activated def activated? raise NotImplementedError end ## # Returns the full path to the base gem directory. # # eg: /usr/local/lib/ruby/gems/1.8 def base_dir raise NotImplementedError end ## # Return true if this spec can require +file+. def contains_requirable_file?(file) if @ignored return false elsif missing_extensions? @ignored = true if Gem::Platform::RUBY == platform || Gem::Platform.local === platform warn "Ignoring #{full_name} because its extensions are not built. " + "Try: gem pristine #{name} --version #{version}" end return false end have_file? file, Gem.suffixes end def default_gem? loaded_from && File.dirname(loaded_from) == Gem.default_specifications_dir end ## # Returns full path to the directory where gem's extensions are installed. def extension_dir @extension_dir ||= File.expand_path(File.join(extensions_dir, full_name)).tap(&Gem::UNTAINT) end ## # Returns path to the extensions directory. def extensions_dir Gem.default_ext_dir_for(base_dir) || File.join(base_dir, 'extensions', Gem::Platform.local.to_s, Gem.extension_api_version) end def find_full_gem_path # :nodoc: # TODO: also, shouldn't it default to full_name if it hasn't been written? path = File.expand_path File.join(gems_dir, full_name) path.tap(&Gem::UNTAINT) path end private :find_full_gem_path ## # The full path to the gem (install path + full name). def full_gem_path # TODO: This is a heavily used method by gems, so we'll need # to aleast just alias it to #gem_dir rather than remove it. @full_gem_path ||= find_full_gem_path 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. def full_name if platform == Gem::Platform::RUBY or platform.nil? "#{name}-#{version}".dup.tap(&Gem::UNTAINT) else "#{name}-#{version}-#{platform}".dup.tap(&Gem::UNTAINT) end end ## # Full paths in the gem to add to <code>$LOAD_PATH</code> when this gem is # activated. def full_require_paths @full_require_paths ||= begin full_paths = raw_require_paths.map do |path| File.join full_gem_path, path.tap(&Gem::UNTAINT) end full_paths << extension_dir if have_extensions? full_paths end end ## # The path to the data directory for this gem. def datadir # TODO: drop the extra ", gem_name" which is uselessly redundant File.expand_path(File.join(gems_dir, full_name, "data", name)).tap(&Gem::UNTAINT) end ## # Full path of the target library file. # If the file is not in this gem, return nil. def to_fullpath(path) if activated? @paths_map ||= {} @paths_map[path] ||= begin fullpath = nil suffixes = Gem.suffixes suffixes.find do |suf| full_require_paths.find do |dir| File.file?(fullpath = "#{dir}/#{path}#{suf}") end end ? fullpath : nil end else nil end end ## # Returns the full path to this spec's gem directory. # eg: /usr/local/lib/ruby/1.8/gems/mygem-1.0 def gem_dir @gem_dir ||= File.expand_path File.join(gems_dir, full_name) end ## # Returns the full path to the gems directory containing this spec's # gem directory. eg: /usr/local/lib/ruby/1.8/gems def gems_dir raise NotImplementedError end def internal_init # :nodoc: @extension_dir = nil @full_gem_path = nil @gem_dir = nil @ignored = nil end ## # Name of the gem def name raise NotImplementedError end ## # Platform of the gem def platform raise NotImplementedError end def raw_require_paths # :nodoc: raise NotImplementedError end ## # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is # activated. # # See also #require_paths= # # If you have an extension you do not need to add <code>"ext"</code> to the # require path, the extension build process will copy the extension files # into "lib" for you. # # The default value is <code>"lib"</code> # # Usage: # # # If all library files are in the root directory... # spec.require_path = '.' def require_paths return raw_require_paths unless have_extensions? [extension_dir].concat raw_require_paths end ## # Returns the paths to the source files for use with analysis and # documentation tools. These paths are relative to full_gem_path. def source_paths paths = raw_require_paths.dup if have_extensions? ext_dirs = extensions.map do |extension| extension.split(File::SEPARATOR, 2).first end.uniq paths.concat ext_dirs end paths.uniq end ## # Return all files in this gem that match for +glob+. def matches_for_glob(glob) # TODO: rename? glob = File.join(self.lib_dirs_glob, glob) Dir[glob].map {|f| f.tap(&Gem::UNTAINT) } # FIX our tests are broken, run w/ SAFE=1 end ## # Returns the list of plugins in this spec. def plugins matches_for_glob("rubygems#{Gem.plugin_suffix_pattern}") end ## # Returns a string usable in Dir.glob to match all requirable paths # for this spec. def lib_dirs_glob dirs = if self.raw_require_paths if self.raw_require_paths.size > 1 "{#{self.raw_require_paths.join(',')}}" else self.raw_require_paths.first end else "lib" # default value for require_paths for bundler/inline end "#{self.full_gem_path}/#{dirs}".dup.tap(&Gem::UNTAINT) end ## # Return a Gem::Specification from this gem def to_spec raise NotImplementedError end ## # Version of the gem def version raise NotImplementedError end ## # Whether this specification is stubbed - i.e. we have information # about the gem from a stub line, without having to evaluate the # entire gemspec file. def stubbed? raise NotImplementedError end def this; self; end private def have_extensions?; !extensions.empty?; end def have_file?(file, suffixes) return true if raw_require_paths.any? do |path| base = File.join(gems_dir, full_name, path.tap(&Gem::UNTAINT), file).tap(&Gem::UNTAINT) suffixes.any? {|suf| File.file? base + suf } end if have_extensions? base = File.join extension_dir, file suffixes.any? {|suf| File.file? base + suf } else false end end end man/man5/gemfile.5 0000644 00000053300 15102661023 0007652 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "GEMFILE" "5" "December 2021" "" "" . .SH "NAME" \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs . .SH "SYNOPSIS" A \fBGemfile\fR describes the gem dependencies required to execute associated Ruby code\. . .P Place the \fBGemfile\fR in the root of the directory containing the associated code\. For instance, in a Rails application, place the \fBGemfile\fR in the same directory as the \fBRakefile\fR\. . .SH "SYNTAX" A \fBGemfile\fR is evaluated as Ruby code, in a context which makes available a number of methods used to describe the gem requirements\. . .SH "GLOBAL SOURCES" At the top of the \fBGemfile\fR, add a line for the \fBRubygems\fR source that contains the gems listed in the \fBGemfile\fR\. . .IP "" 4 . .nf source "https://rubygems\.org" . .fi . .IP "" 0 . .P It is possible, but not recommended as of Bundler 1\.7, to add multiple global \fBsource\fR lines\. Each of these \fBsource\fRs \fBMUST\fR be a valid Rubygems repository\. . .P Sources are checked for gems following the heuristics described in \fISOURCE PRIORITY\fR\. If a gem is found in more than one global source, Bundler will print a warning after installing the gem indicating which source was used, and listing the other sources where the gem is available\. A specific source can be selected for gems that need to use a non\-standard repository, suppressing this warning, by using the \fI\fB:source\fR option\fR or a \fI\fBsource\fR block\fR\. . .SS "CREDENTIALS" Some gem sources require a username and password\. Use bundle config(1) \fIbundle\-config\.1\.html\fR to set the username and password for any of the sources that need it\. The command must be run once on each computer that will install the Gemfile, but this keeps the credentials from being stored in plain text in version control\. . .IP "" 4 . .nf bundle config gems\.example\.com user:password . .fi . .IP "" 0 . .P For some sources, like a company Gemfury account, it may be easier to include the credentials in the Gemfile as part of the source URL\. . .IP "" 4 . .nf source "https://user:password@gems\.example\.com" . .fi . .IP "" 0 . .P Credentials in the source URL will take precedence over credentials set using \fBconfig\fR\. . .SH "RUBY" If your application requires a specific Ruby version or engine, specify your requirements using the \fBruby\fR method, with the following arguments\. All parameters are \fBOPTIONAL\fR unless otherwise specified\. . .SS "VERSION (required)" The version of Ruby that your application requires\. If your application requires an alternate Ruby engine, such as JRuby, Rubinius or TruffleRuby, this should be the Ruby version that the engine is compatible with\. . .IP "" 4 . .nf ruby "1\.9\.3" . .fi . .IP "" 0 . .SS "ENGINE" Each application \fImay\fR specify a Ruby engine\. If an engine is specified, an engine version \fImust\fR also be specified\. . .P What exactly is an Engine? \- A Ruby engine is an implementation of the Ruby language\. . .IP "\(bu" 4 For background: the reference or original implementation of the Ruby programming language is called Matz\'s Ruby Interpreter \fIhttps://en\.wikipedia\.org/wiki/Ruby_MRI\fR, or MRI for short\. This is named after Ruby creator Yukihiro Matsumoto, also known as Matz\. MRI is also known as CRuby, because it is written in C\. MRI is the most widely used Ruby engine\. . .IP "\(bu" 4 Other implementations \fIhttps://www\.ruby\-lang\.org/en/about/\fR of Ruby exist\. Some of the more well\-known implementations include Rubinius \fIhttps://rubinius\.com/\fR, and JRuby \fIhttp://jruby\.org/\fR\. Rubinius is an alternative implementation of Ruby written in Ruby\. JRuby is an implementation of Ruby on the JVM, short for Java Virtual Machine\. . .IP "" 0 . .SS "ENGINE VERSION" Each application \fImay\fR specify a Ruby engine version\. If an engine version is specified, an engine \fImust\fR also be specified\. If the engine is "ruby" the engine version specified \fImust\fR match the Ruby version\. . .IP "" 4 . .nf ruby "1\.8\.7", :engine => "jruby", :engine_version => "1\.6\.7" . .fi . .IP "" 0 . .SS "PATCHLEVEL" Each application \fImay\fR specify a Ruby patchlevel\. . .IP "" 4 . .nf ruby "2\.0\.0", :patchlevel => "247" . .fi . .IP "" 0 . .SH "GEMS" Specify gem requirements using the \fBgem\fR method, with the following arguments\. All parameters are \fBOPTIONAL\fR unless otherwise specified\. . .SS "NAME (required)" For each gem requirement, list a single \fIgem\fR line\. . .IP "" 4 . .nf gem "nokogiri" . .fi . .IP "" 0 . .SS "VERSION" Each \fIgem\fR \fBMAY\fR have one or more version specifiers\. . .IP "" 4 . .nf gem "nokogiri", ">= 1\.4\.2" gem "RedCloth", ">= 4\.1\.0", "< 4\.2\.0" . .fi . .IP "" 0 . .SS "REQUIRE AS" Each \fIgem\fR \fBMAY\fR specify files that should be used when autorequiring via \fBBundler\.require\fR\. You may pass an array with multiple files or \fBtrue\fR if the file you want \fBrequired\fR has the same name as \fIgem\fR or \fBfalse\fR to prevent any file from being autorequired\. . .IP "" 4 . .nf gem "redis", :require => ["redis/connection/hiredis", "redis"] gem "webmock", :require => false gem "byebug", :require => true . .fi . .IP "" 0 . .P The argument defaults to the name of the gem\. For example, these are identical: . .IP "" 4 . .nf gem "nokogiri" gem "nokogiri", :require => "nokogiri" gem "nokogiri", :require => true . .fi . .IP "" 0 . .SS "GROUPS" Each \fIgem\fR \fBMAY\fR specify membership in one or more groups\. Any \fIgem\fR that does not specify membership in any group is placed in the \fBdefault\fR group\. . .IP "" 4 . .nf gem "rspec", :group => :test gem "wirble", :groups => [:development, :test] . .fi . .IP "" 0 . .P The Bundler runtime allows its two main methods, \fBBundler\.setup\fR and \fBBundler\.require\fR, to limit their impact to particular groups\. . .IP "" 4 . .nf # setup adds gems to Ruby\'s load path Bundler\.setup # defaults to all groups require "bundler/setup" # same as Bundler\.setup Bundler\.setup(:default) # only set up the _default_ group Bundler\.setup(:test) # only set up the _test_ group (but `not` _default_) Bundler\.setup(:default, :test) # set up the _default_ and _test_ groups, but no others # require requires all of the gems in the specified groups Bundler\.require # defaults to the _default_ group Bundler\.require(:default) # identical Bundler\.require(:default, :test) # requires the _default_ and _test_ groups Bundler\.require(:test) # requires the _test_ group . .fi . .IP "" 0 . .P The Bundler CLI allows you to specify a list of groups whose gems \fBbundle install\fR should not install with the \fBwithout\fR configuration\. . .P To specify multiple groups to ignore, specify a list of groups separated by spaces\. . .IP "" 4 . .nf bundle config set \-\-local without test bundle config set \-\-local without development test . .fi . .IP "" 0 . .P Also, calling \fBBundler\.setup\fR with no parameters, or calling \fBrequire "bundler/setup"\fR will setup all groups except for the ones you excluded via \fB\-\-without\fR (since they are not available)\. . .P Note that on \fBbundle install\fR, bundler downloads and evaluates all gems, in order to create a single canonical list of all of the required gems and their dependencies\. This means that you cannot list different versions of the same gems in different groups\. For more details, see Understanding Bundler \fIhttps://bundler\.io/rationale\.html\fR\. . .SS "PLATFORMS" If a gem should only be used in a particular platform or set of platforms, you can specify them\. Platforms are essentially identical to groups, except that you do not need to use the \fB\-\-without\fR install\-time flag to exclude groups of gems for other platforms\. . .P There are a number of \fBGemfile\fR platforms: . .TP \fBruby\fR C Ruby (MRI), Rubinius or TruffleRuby, but \fBNOT\fR Windows . .TP \fBmri\fR Same as \fIruby\fR, but only C Ruby (MRI) . .TP \fBmingw\fR Windows 32 bit \'mingw32\' platform (aka RubyInstaller) . .TP \fBx64_mingw\fR Windows 64 bit \'mingw32\' platform (aka RubyInstaller x64) . .TP \fBrbx\fR Rubinius . .TP \fBjruby\fR JRuby . .TP \fBtruffleruby\fR TruffleRuby . .TP \fBmswin\fR Windows . .P You can restrict further by platform and version for all platforms \fIexcept\fR for \fBrbx\fR, \fBjruby\fR, \fBtruffleruby\fR and \fBmswin\fR\. . .P To specify a version in addition to a platform, append the version number without the delimiter to the platform\. For example, to specify that a gem should only be used on platforms with Ruby 2\.3, use: . .IP "" 4 . .nf ruby_23 . .fi . .IP "" 0 . .P The full list of platforms and supported versions includes: . .TP \fBruby\fR 1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 . .TP \fBmri\fR 1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 . .TP \fBmingw\fR 1\.8, 1\.9, 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 . .TP \fBx64_mingw\fR 2\.0, 2\.1, 2\.2, 2\.3, 2\.4, 2\.5, 2\.6 . .P As with groups, you can specify one or more platforms: . .IP "" 4 . .nf gem "weakling", :platforms => :jruby gem "ruby\-debug", :platforms => :mri_18 gem "nokogiri", :platforms => [:mri_18, :jruby] . .fi . .IP "" 0 . .P All operations involving groups (\fBbundle install\fR \fIbundle\-install\.1\.html\fR, \fBBundler\.setup\fR, \fBBundler\.require\fR) behave exactly the same as if any groups not matching the current platform were explicitly excluded\. . .SS "SOURCE" You can select an alternate Rubygems repository for a gem using the \':source\' option\. . .IP "" 4 . .nf gem "some_internal_gem", :source => "https://gems\.example\.com" . .fi . .IP "" 0 . .P This forces the gem to be loaded from this source and ignores any global sources declared at the top level of the file\. If the gem does not exist in this source, it will not be installed\. . .P Bundler will search for child dependencies of this gem by first looking in the source selected for the parent, but if they are not found there, it will fall back on global sources using the ordering described in \fISOURCE PRIORITY\fR\. . .P Selecting a specific source repository this way also suppresses the ambiguous gem warning described above in \fIGLOBAL SOURCES (#source)\fR\. . .P Using the \fB:source\fR option for an individual gem will also make that source available as a possible global source for any other gems which do not specify explicit sources\. Thus, when adding gems with explicit sources, it is recommended that you also ensure all other gems in the Gemfile are using explicit sources\. . .SS "GIT" If necessary, you can specify that a gem is located at a particular git repository using the \fB:git\fR parameter\. The repository can be accessed via several protocols: . .TP \fBHTTP(S)\fR gem "rails", :git => "https://github\.com/rails/rails\.git" . .TP \fBSSH\fR gem "rails", :git => "git@github\.com:rails/rails\.git" . .TP \fBgit\fR gem "rails", :git => "git://github\.com/rails/rails\.git" . .P If using SSH, the user that you use to run \fBbundle install\fR \fBMUST\fR have the appropriate keys available in their \fB$HOME/\.ssh\fR\. . .P \fBNOTE\fR: \fBhttp://\fR and \fBgit://\fR URLs should be avoided if at all possible\. These protocols are unauthenticated, so a man\-in\-the\-middle attacker can deliver malicious code and compromise your system\. HTTPS and SSH are strongly preferred\. . .P The \fBgroup\fR, \fBplatforms\fR, and \fBrequire\fR options are available and behave exactly the same as they would for a normal gem\. . .P A git repository \fBSHOULD\fR have at least one file, at the root of the directory containing the gem, with the extension \fB\.gemspec\fR\. This file \fBMUST\fR contain a valid gem specification, as expected by the \fBgem build\fR command\. . .P If a git repository does not have a \fB\.gemspec\fR, bundler will attempt to create one, but it will not contain any dependencies, executables, or C extension compilation instructions\. As a result, it may fail to properly integrate into your application\. . .P If a git repository does have a \fB\.gemspec\fR for the gem you attached it to, a version specifier, if provided, means that the git repository is only valid if the \fB\.gemspec\fR specifies a version matching the version specifier\. If not, bundler will print a warning\. . .IP "" 4 . .nf gem "rails", "2\.3\.8", :git => "https://github\.com/rails/rails\.git" # bundle install will fail, because the \.gemspec in the rails # repository\'s master branch specifies version 3\.0\.0 . .fi . .IP "" 0 . .P If a git repository does \fBnot\fR have a \fB\.gemspec\fR for the gem you attached it to, a version specifier \fBMUST\fR be provided\. Bundler will use this version in the simple \fB\.gemspec\fR it creates\. . .P Git repositories support a number of additional options\. . .TP \fBbranch\fR, \fBtag\fR, and \fBref\fR You \fBMUST\fR only specify at most one of these options\. The default is \fB:branch => "master"\fR\. For example: . .IP gem "rails", :git => "https://github\.com/rails/rails\.git", :branch => "5\-0\-stable" . .IP gem "rails", :git => "https://github\.com/rails/rails\.git", :tag => "v5\.0\.0" . .IP gem "rails", :git => "https://github\.com/rails/rails\.git", :ref => "4aded" . .TP \fBsubmodules\fR For reference, a git submodule \fIhttps://git\-scm\.com/book/en/v2/Git\-Tools\-Submodules\fR lets you have another git repository within a subfolder of your repository\. Specify \fB:submodules => true\fR to cause bundler to expand any submodules included in the git repository . .P If a git repository contains multiple \fB\.gemspecs\fR, each \fB\.gemspec\fR represents a gem located at the same place in the file system as the \fB\.gemspec\fR\. . .IP "" 4 . .nf |~rails [git root] | |\-rails\.gemspec [rails gem located here] |~actionpack | |\-actionpack\.gemspec [actionpack gem located here] |~activesupport | |\-activesupport\.gemspec [activesupport gem located here] |\.\.\. . .fi . .IP "" 0 . .P To install a gem located in a git repository, bundler changes to the directory containing the gemspec, runs \fBgem build name\.gemspec\fR and then installs the resulting gem\. The \fBgem build\fR command, which comes standard with Rubygems, evaluates the \fB\.gemspec\fR in the context of the directory in which it is located\. . .SS "GIT SOURCE" A custom git source can be defined via the \fBgit_source\fR method\. Provide the source\'s name as an argument, and a block which receives a single argument and interpolates it into a string to return the full repo address: . .IP "" 4 . .nf git_source(:stash){ |repo_name| "https://stash\.corp\.acme\.pl/#{repo_name}\.git" } gem \'rails\', :stash => \'forks/rails\' . .fi . .IP "" 0 . .P In addition, if you wish to choose a specific branch: . .IP "" 4 . .nf gem "rails", :stash => "forks/rails", :branch => "branch_name" . .fi . .IP "" 0 . .SS "GITHUB" \fBNOTE\fR: This shorthand should be avoided until Bundler 2\.0, since it currently expands to an insecure \fBgit://\fR URL\. This allows a man\-in\-the\-middle attacker to compromise your system\. . .P If the git repository you want to use is hosted on GitHub and is public, you can use the :github shorthand to specify the github username and repository name (without the trailing "\.git"), separated by a slash\. If both the username and repository name are the same, you can omit one\. . .IP "" 4 . .nf gem "rails", :github => "rails/rails" gem "rails", :github => "rails" . .fi . .IP "" 0 . .P Are both equivalent to . .IP "" 4 . .nf gem "rails", :git => "git://github\.com/rails/rails\.git" . .fi . .IP "" 0 . .P Since the \fBgithub\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\. . .P You can also directly pass a pull request URL: . .IP "" 4 . .nf gem "rails", :github => "https://github\.com/rails/rails/pull/43753" . .fi . .IP "" 0 . .P Which is equivalent to: . .IP "" 4 . .nf gem "rails", :github => "rails/rails", branch: "refs/pull/43753/head" . .fi . .IP "" 0 . .SS "GIST" If the git repository you want to use is hosted as a Github Gist and is public, you can use the :gist shorthand to specify the gist identifier (without the trailing "\.git")\. . .IP "" 4 . .nf gem "the_hatch", :gist => "4815162342" . .fi . .IP "" 0 . .P Is equivalent to: . .IP "" 4 . .nf gem "the_hatch", :git => "https://gist\.github\.com/4815162342\.git" . .fi . .IP "" 0 . .P Since the \fBgist\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\. . .SS "BITBUCKET" If the git repository you want to use is hosted on Bitbucket and is public, you can use the :bitbucket shorthand to specify the bitbucket username and repository name (without the trailing "\.git"), separated by a slash\. If both the username and repository name are the same, you can omit one\. . .IP "" 4 . .nf gem "rails", :bitbucket => "rails/rails" gem "rails", :bitbucket => "rails" . .fi . .IP "" 0 . .P Are both equivalent to . .IP "" 4 . .nf gem "rails", :git => "https://rails@bitbucket\.org/rails/rails\.git" . .fi . .IP "" 0 . .P Since the \fBbitbucket\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\. . .SS "PATH" You can specify that a gem is located in a particular location on the file system\. Relative paths are resolved relative to the directory containing the \fBGemfile\fR\. . .P Similar to the semantics of the \fB:git\fR option, the \fB:path\fR option requires that the directory in question either contains a \fB\.gemspec\fR for the gem, or that you specify an explicit version that bundler should use\. . .P Unlike \fB:git\fR, bundler does not compile C extensions for gems specified as paths\. . .IP "" 4 . .nf gem "rails", :path => "vendor/rails" . .fi . .IP "" 0 . .P If you would like to use multiple local gems directly from the filesystem, you can set a global \fBpath\fR option to the path containing the gem\'s files\. This will automatically load gemspec files from subdirectories\. . .IP "" 4 . .nf path \'components\' do gem \'admin_ui\' gem \'public_ui\' end . .fi . .IP "" 0 . .SH "BLOCK FORM OF SOURCE, GIT, PATH, GROUP and PLATFORMS" The \fB:source\fR, \fB:git\fR, \fB:path\fR, \fB:group\fR, and \fB:platforms\fR options may be applied to a group of gems by using block form\. . .IP "" 4 . .nf source "https://gems\.example\.com" do gem "some_internal_gem" gem "another_internal_gem" end git "https://github\.com/rails/rails\.git" do gem "activesupport" gem "actionpack" end platforms :ruby do gem "ruby\-debug" gem "sqlite3" end group :development, :optional => true do gem "wirble" gem "faker" end . .fi . .IP "" 0 . .P In the case of the group block form the :optional option can be given to prevent a group from being installed unless listed in the \fB\-\-with\fR option given to the \fBbundle install\fR command\. . .P In the case of the \fBgit\fR block form, the \fB:ref\fR, \fB:branch\fR, \fB:tag\fR, and \fB:submodules\fR options may be passed to the \fBgit\fR method, and all gems in the block will inherit those options\. . .P The presence of a \fBsource\fR block in a Gemfile also makes that source available as a possible global source for any other gems which do not specify explicit sources\. Thus, when defining source blocks, it is recommended that you also ensure all other gems in the Gemfile are using explicit sources, either via source blocks or \fB:source\fR directives on individual gems\. . .SH "INSTALL_IF" The \fBinstall_if\fR method allows gems to be installed based on a proc or lambda\. This is especially useful for optional gems that can only be used if certain software is installed or some other conditions are met\. . .IP "" 4 . .nf install_if \-> { RUBY_PLATFORM =~ /darwin/ } do gem "pasteboard" end . .fi . .IP "" 0 . .SH "GEMSPEC" The \fB\.gemspec\fR \fIhttp://guides\.rubygems\.org/specification\-reference/\fR file is where you provide metadata about your gem to Rubygems\. Some required Gemspec attributes include the name, description, and homepage of your gem\. This is also where you specify the dependencies your gem needs to run\. . .P If you wish to use Bundler to help install dependencies for a gem while it is being developed, use the \fBgemspec\fR method to pull in the dependencies listed in the \fB\.gemspec\fR file\. . .P The \fBgemspec\fR method adds any runtime dependencies as gem requirements in the default group\. It also adds development dependencies as gem requirements in the \fBdevelopment\fR group\. Finally, it adds a gem requirement on your project (\fB:path => \'\.\'\fR)\. In conjunction with \fBBundler\.setup\fR, this allows you to require project files in your test code as you would if the project were installed as a gem; you need not manipulate the load path manually or require project files via relative paths\. . .P The \fBgemspec\fR method supports optional \fB:path\fR, \fB:glob\fR, \fB:name\fR, and \fB:development_group\fR options, which control where bundler looks for the \fB\.gemspec\fR, the glob it uses to look for the gemspec (defaults to: "{,\fI,\fR/*}\.gemspec"), what named \fB\.gemspec\fR it uses (if more than one is present), and which group development dependencies are included in\. . .P When a \fBgemspec\fR dependency encounters version conflicts during resolution, the local version under development will always be selected \-\- even if there are remote versions that better match other requirements for the \fBgemspec\fR gem\. . .SH "SOURCE PRIORITY" When attempting to locate a gem to satisfy a gem requirement, bundler uses the following priority order: . .IP "1." 4 The source explicitly attached to the gem (using \fB:source\fR, \fB:path\fR, or \fB:git\fR) . .IP "2." 4 For implicit gems (dependencies of explicit gems), any source, git, or path repository declared on the parent\. This results in bundler prioritizing the ActiveSupport gem from the Rails git repository over ones from \fBrubygems\.org\fR . .IP "3." 4 The sources specified via global \fBsource\fR lines, searching each source in your \fBGemfile\fR from last added to first added\. . .IP "" 0 man/man1/bundle-doctor.1 0000644 00000002215 15102661023 0010772 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-DOCTOR" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-doctor\fR \- Checks the bundle for common problems . .SH "SYNOPSIS" \fBbundle doctor\fR [\-\-quiet] [\-\-gemfile=GEMFILE] . .SH "DESCRIPTION" Checks your Gemfile and gem environment for common problems\. If issues are detected, Bundler prints them and exits status 1\. Otherwise, Bundler prints a success message and exits status 0\. . .P Examples of common problems caught by bundle\-doctor include: . .IP "\(bu" 4 Invalid Bundler settings . .IP "\(bu" 4 Mismatched Ruby versions . .IP "\(bu" 4 Mismatched platforms . .IP "\(bu" 4 Uninstalled gems . .IP "\(bu" 4 Missing dependencies . .IP "" 0 . .SH "OPTIONS" . .TP \fB\-\-quiet\fR Only output warnings and errors\. . .TP \fB\-\-gemfile=<gemfile>\fR The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project\'s root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\. man/man1/bundle-exec.1 0000644 00000015152 15102661023 0010430 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-EXEC" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-exec\fR \- Execute a command in the context of the bundle . .SH "SYNOPSIS" \fBbundle exec\fR [\-\-keep\-file\-descriptors] \fIcommand\fR . .SH "DESCRIPTION" This command executes the command, making all gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] available to \fBrequire\fR in Ruby programs\. . .P Essentially, if you would normally have run something like \fBrspec spec/my_spec\.rb\fR, and you want to use the gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] and installed via bundle install(1) \fIbundle\-install\.1\.html\fR, you should run \fBbundle exec rspec spec/my_spec\.rb\fR\. . .P Note that \fBbundle exec\fR does not require that an executable is available on your shell\'s \fB$PATH\fR\. . .SH "OPTIONS" . .TP \fB\-\-keep\-file\-descriptors\fR Exec in Ruby 2\.0 began discarding non\-standard file descriptors\. When this flag is passed, exec will revert to the 1\.9 behaviour of passing all file descriptors to the new process\. . .SH "BUNDLE INSTALL \-\-BINSTUBS" If you use the \fB\-\-binstubs\fR flag in bundle install(1) \fIbundle\-install\.1\.html\fR, Bundler will automatically create a directory (which defaults to \fBapp_root/bin\fR) containing all of the executables available from gems in the bundle\. . .P After using \fB\-\-binstubs\fR, \fBbin/rspec spec/my_spec\.rb\fR is identical to \fBbundle exec rspec spec/my_spec\.rb\fR\. . .SH "ENVIRONMENT MODIFICATIONS" \fBbundle exec\fR makes a number of changes to the shell environment, then executes the command you specify in full\. . .IP "\(bu" 4 make sure that it\'s still possible to shell out to \fBbundle\fR from inside a command invoked by \fBbundle exec\fR (using \fB$BUNDLE_BIN_PATH\fR) . .IP "\(bu" 4 put the directory containing executables (like \fBrails\fR, \fBrspec\fR, \fBrackup\fR) for your bundle on \fB$PATH\fR . .IP "\(bu" 4 make sure that if bundler is invoked in the subshell, it uses the same \fBGemfile\fR (by setting \fBBUNDLE_GEMFILE\fR) . .IP "\(bu" 4 add \fB\-rbundler/setup\fR to \fB$RUBYOPT\fR, which makes sure that Ruby programs invoked in the subshell can see the gems in the bundle . .IP "" 0 . .P It also modifies Rubygems: . .IP "\(bu" 4 disallow loading additional gems not in the bundle . .IP "\(bu" 4 modify the \fBgem\fR method to be a no\-op if a gem matching the requirements is in the bundle, and to raise a \fBGem::LoadError\fR if it\'s not . .IP "\(bu" 4 Define \fBGem\.refresh\fR to be a no\-op, since the source index is always frozen when using bundler, and to prevent gems from the system leaking into the environment . .IP "\(bu" 4 Override \fBGem\.bin_path\fR to use the gems in the bundle, making system executables work . .IP "\(bu" 4 Add all gems in the bundle into Gem\.loaded_specs . .IP "" 0 . .P Finally, \fBbundle exec\fR also implicitly modifies \fBGemfile\.lock\fR if the lockfile and the Gemfile do not match\. Bundler needs the Gemfile to determine things such as a gem\'s groups, \fBautorequire\fR, and platforms, etc\., and that information isn\'t stored in the lockfile\. The Gemfile and lockfile must be synced in order to \fBbundle exec\fR successfully, so \fBbundle exec\fR updates the lockfile beforehand\. . .SS "Loading" By default, when attempting to \fBbundle exec\fR to a file with a ruby shebang, Bundler will \fBKernel\.load\fR that file instead of using \fBKernel\.exec\fR\. For the vast majority of cases, this is a performance improvement\. In a rare few cases, this could cause some subtle side\-effects (such as dependence on the exact contents of \fB$0\fR or \fB__FILE__\fR) and the optimization can be disabled by enabling the \fBdisable_exec_load\fR setting\. . .SS "Shelling out" Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_clean_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don\'t work inside a bundle: . .IP "" 4 . .nf Bundler\.with_clean_env do `brew install wget` end . .fi . .IP "" 0 . .P Using \fBwith_clean_env\fR is also necessary if you are shelling out to a different bundle\. Any Bundler commands run in a subshell will inherit the current Gemfile, so commands that need to run in the context of a different bundle also need to use \fBwith_clean_env\fR\. . .IP "" 4 . .nf Bundler\.with_clean_env do Dir\.chdir "/other/bundler/project" do `bundle exec \./script` end end . .fi . .IP "" 0 . .P Bundler provides convenience helpers that wrap \fBsystem\fR and \fBexec\fR, and they can be used like this: . .IP "" 4 . .nf Bundler\.clean_system(\'brew install wget\') Bundler\.clean_exec(\'brew install wget\') . .fi . .IP "" 0 . .SH "RUBYGEMS PLUGINS" At present, the Rubygems plugin system requires all files named \fBrubygems_plugin\.rb\fR on the load path of \fIany\fR installed gem when any Ruby code requires \fBrubygems\.rb\fR\. This includes executables installed into the system, like \fBrails\fR, \fBrackup\fR, and \fBrspec\fR\. . .P Since Rubygems plugins can contain arbitrary Ruby code, they commonly end up activating themselves or their dependencies\. . .P For instance, the \fBgemcutter 0\.5\fR gem depended on \fBjson_pure\fR\. If you had that version of gemcutter installed (even if you \fIalso\fR had a newer version without this problem), Rubygems would activate \fBgemcutter 0\.5\fR and \fBjson_pure <latest>\fR\. . .P If your Gemfile(5) also contained \fBjson_pure\fR (or a gem with a dependency on \fBjson_pure\fR), the latest version on your system might conflict with the version in your Gemfile(5), or the snapshot version in your \fBGemfile\.lock\fR\. . .P If this happens, bundler will say: . .IP "" 4 . .nf You have already activated json_pure 1\.4\.6 but your Gemfile requires json_pure 1\.4\.3\. Consider using bundle exec\. . .fi . .IP "" 0 . .P In this situation, you almost certainly want to remove the underlying gem with the problematic gem plugin\. In general, the authors of these plugins (in this case, the \fBgemcutter\fR gem) have released newer versions that are more careful in their plugins\. . .P You can find a list of all the gems containing gem plugins by running . .IP "" 4 . .nf ruby \-rrubygems \-e "puts Gem\.find_files(\'rubygems_plugin\.rb\')" . .fi . .IP "" 0 . .P At the very least, you should remove all but the newest version of each gem plugin, and also remove all gem plugins that you aren\'t using (\fBgem uninstall gem_name\fR)\. man/man1/bundle-info.1 0000644 00000000701 15102661023 0010431 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-INFO" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-info\fR \- Show information for the given gem in your bundle . .SH "SYNOPSIS" \fBbundle info\fR [GEM] [\-\-path] . .SH "DESCRIPTION" Print the basic information about the provided GEM such as homepage, version, path and summary\. . .SH "OPTIONS" . .TP \fB\-\-path\fR Print the path of the given gem man/man1/bundle-update.1 0000644 00000033073 15102661023 0010770 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-UPDATE" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-update\fR \- Update your gems to the latest available versions . .SH "SYNOPSIS" \fBbundle update\fR \fI*gems\fR [\-\-all] [\-\-group=NAME] [\-\-source=NAME] [\-\-local] [\-\-ruby] [\-\-bundler[=VERSION]] [\-\-full\-index] [\-\-jobs=JOBS] [\-\-quiet] [\-\-patch|\-\-minor|\-\-major] [\-\-redownload] [\-\-strict] [\-\-conservative] . .SH "DESCRIPTION" Update the gems specified (all gems, if \fB\-\-all\fR flag is used), ignoring the previously installed gems specified in the \fBGemfile\.lock\fR\. In general, you should use bundle install(1) \fIbundle\-install\.1\.html\fR to install the same exact gems and versions across machines\. . .P You would use \fBbundle update\fR to explicitly update the version of a gem\. . .SH "OPTIONS" . .TP \fB\-\-all\fR Update all gems specified in Gemfile\. . .TP \fB\-\-group=<name>\fR, \fB\-g=[<name>]\fR Only update the gems in the specified group\. For instance, you can update all gems in the development group with \fBbundle update \-\-group development\fR\. You can also call \fBbundle update rails \-\-group test\fR to update the rails gem and all gems in the test group, for example\. . .TP \fB\-\-source=<name>\fR The name of a \fB:git\fR or \fB:path\fR source used in the Gemfile(5)\. For instance, with a \fB:git\fR source of \fBhttp://github\.com/rails/rails\.git\fR, you would call \fBbundle update \-\-source rails\fR . .TP \fB\-\-local\fR Do not attempt to fetch gems remotely and use the gem cache instead\. . .TP \fB\-\-ruby\fR Update the locked version of Ruby to the current version of Ruby\. . .TP \fB\-\-bundler\fR Update the locked version of bundler to the invoked bundler version\. . .TP \fB\-\-full\-index\fR Fall back to using the single\-file index of all gems\. . .TP \fB\-\-jobs=[<number>]\fR, \fB\-j[<number>]\fR Specify the number of jobs to run in parallel\. The default is \fB1\fR\. . .TP \fB\-\-retry=[<number>]\fR Retry failed network or git requests for \fInumber\fR times\. . .TP \fB\-\-quiet\fR Only output warnings and errors\. . .TP \fB\-\-redownload\fR Force downloading every gem\. . .TP \fB\-\-patch\fR Prefer updating only to next patch version\. . .TP \fB\-\-minor\fR Prefer updating only to next minor version\. . .TP \fB\-\-major\fR Prefer updating to next major version (default)\. . .TP \fB\-\-strict\fR Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR | \fB\-\-major\fR\. . .TP \fB\-\-conservative\fR Use bundle install conservative update behavior and do not allow indirect dependencies to be updated\. . .SH "UPDATING ALL GEMS" If you run \fBbundle update \-\-all\fR, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\. . .P Consider the following Gemfile(5): . .IP "" 4 . .nf source "https://rubygems\.org" gem "rails", "3\.0\.0\.rc" gem "nokogiri" . .fi . .IP "" 0 . .P When you run bundle install(1) \fIbundle\-install\.1\.html\fR the first time, bundler will resolve all of the dependencies, all the way down, and install what you need: . .IP "" 4 . .nf Fetching gem metadata from https://rubygems\.org/\.\.\.\.\.\.\.\.\. Resolving dependencies\.\.\. Installing builder 2\.1\.2 Installing abstract 1\.0\.0 Installing rack 1\.2\.8 Using bundler 1\.7\.6 Installing rake 10\.4\.0 Installing polyglot 0\.3\.5 Installing mime\-types 1\.25\.1 Installing i18n 0\.4\.2 Installing mini_portile 0\.6\.1 Installing tzinfo 0\.3\.42 Installing rack\-mount 0\.6\.14 Installing rack\-test 0\.5\.7 Installing treetop 1\.4\.15 Installing thor 0\.14\.6 Installing activesupport 3\.0\.0\.rc Installing erubis 2\.6\.6 Installing activemodel 3\.0\.0\.rc Installing arel 0\.4\.0 Installing mail 2\.2\.20 Installing activeresource 3\.0\.0\.rc Installing actionpack 3\.0\.0\.rc Installing activerecord 3\.0\.0\.rc Installing actionmailer 3\.0\.0\.rc Installing railties 3\.0\.0\.rc Installing rails 3\.0\.0\.rc Installing nokogiri 1\.6\.5 Bundle complete! 2 Gemfile dependencies, 26 gems total\. Use `bundle show [gemname]` to see where a bundled gem is installed\. . .fi . .IP "" 0 . .P As you can see, even though you have two gems in the Gemfile(5), your application needs 26 different gems in order to run\. Bundler remembers the exact versions it installed in \fBGemfile\.lock\fR\. The next time you run bundle install(1) \fIbundle\-install\.1\.html\fR, bundler skips the dependency resolution and installs the same gems as it installed last time\. . .P After checking in the \fBGemfile\.lock\fR into version control and cloning it on another machine, running bundle install(1) \fIbundle\-install\.1\.html\fR will \fIstill\fR install the gems that you installed last time\. You don\'t need to worry that a new release of \fBerubis\fR or \fBmail\fR changes the gems you use\. . .P However, from time to time, you might want to update the gems you are using to the newest versions that still match the gems in your Gemfile(5)\. . .P To do this, run \fBbundle update \-\-all\fR, which will ignore the \fBGemfile\.lock\fR, and resolve all the dependencies again\. Keep in mind that this process can result in a significantly different set of the 25 gems, based on the requirements of new gems that the gem authors released since the last time you ran \fBbundle update \-\-all\fR\. . .SH "UPDATING A LIST OF GEMS" Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\. . .P For instance, in the scenario above, imagine that \fBnokogiri\fR releases version \fB1\.4\.4\fR, and you want to update it \fIwithout\fR updating Rails and all of its dependencies\. To do this, run \fBbundle update nokogiri\fR\. . .P Bundler will update \fBnokogiri\fR and any of its dependencies, but leave alone Rails and its dependencies\. . .SH "OVERLAPPING DEPENDENCIES" Sometimes, multiple gems declared in your Gemfile(5) are satisfied by the same second\-level dependency\. For instance, consider the case of \fBthin\fR and \fBrack\-perftools\-profiler\fR\. . .IP "" 4 . .nf source "https://rubygems\.org" gem "thin" gem "rack\-perftools\-profiler" . .fi . .IP "" 0 . .P The \fBthin\fR gem depends on \fBrack >= 1\.0\fR, while \fBrack\-perftools\-profiler\fR depends on \fBrack ~> 1\.0\fR\. If you run bundle install, you get: . .IP "" 4 . .nf Fetching source index for https://rubygems\.org/ Installing daemons (1\.1\.0) Installing eventmachine (0\.12\.10) with native extensions Installing open4 (1\.0\.1) Installing perftools\.rb (0\.4\.7) with native extensions Installing rack (1\.2\.1) Installing rack\-perftools_profiler (0\.0\.2) Installing thin (1\.2\.7) with native extensions Using bundler (1\.0\.0\.rc\.3) . .fi . .IP "" 0 . .P In this case, the two gems have their own set of dependencies, but they share \fBrack\fR in common\. If you run \fBbundle update thin\fR, bundler will update \fBdaemons\fR, \fBeventmachine\fR and \fBrack\fR, which are dependencies of \fBthin\fR, but not \fBopen4\fR or \fBperftools\.rb\fR, which are dependencies of \fBrack\-perftools_profiler\fR\. Note that \fBbundle update thin\fR will update \fBrack\fR even though it\'s \fIalso\fR a dependency of \fBrack\-perftools_profiler\fR\. . .P In short, by default, when you update a gem using \fBbundle update\fR, bundler will update all dependencies of that gem, including those that are also dependencies of another gem\. . .P To prevent updating indirect dependencies, prior to version 1\.14 the only option was the \fBCONSERVATIVE UPDATING\fR behavior in bundle install(1) \fIbundle\-install\.1\.html\fR: . .P In this scenario, updating the \fBthin\fR version manually in the Gemfile(5), and then running bundle install(1) \fIbundle\-install\.1\.html\fR will only update \fBdaemons\fR and \fBeventmachine\fR, but not \fBrack\fR\. For more information, see the \fBCONSERVATIVE UPDATING\fR section of bundle install(1) \fIbundle\-install\.1\.html\fR\. . .P Starting with 1\.14, specifying the \fB\-\-conservative\fR option will also prevent indirect dependencies from being updated\. . .SH "PATCH LEVEL OPTIONS" Version 1\.14 introduced 4 patch\-level options that will influence how gem versions are resolved\. One of the following options can be used: \fB\-\-patch\fR, \fB\-\-minor\fR or \fB\-\-major\fR\. \fB\-\-strict\fR can be added to further influence resolution\. . .TP \fB\-\-patch\fR Prefer updating only to next patch version\. . .TP \fB\-\-minor\fR Prefer updating only to next minor version\. . .TP \fB\-\-major\fR Prefer updating to next major version (default)\. . .TP \fB\-\-strict\fR Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR | \fB\-\-major\fR\. . .P When Bundler is resolving what versions to use to satisfy declared requirements in the Gemfile or in parent gems, it looks up all available versions, filters out any versions that don\'t satisfy the requirement, and then, by default, sorts them from newest to oldest, considering them in that order\. . .P Providing one of the patch level options (e\.g\. \fB\-\-patch\fR) changes the sort order of the satisfying versions, causing Bundler to consider the latest \fB\-\-patch\fR or \fB\-\-minor\fR version available before other versions\. Note that versions outside the stated patch level could still be resolved to if necessary to find a suitable dependency graph\. . .P For example, if gem \'foo\' is locked at 1\.0\.2, with no gem requirement defined in the Gemfile, and versions 1\.0\.3, 1\.0\.4, 1\.1\.0, 1\.1\.1, 2\.0\.0 all exist, the default order of preference by default (\fB\-\-major\fR) will be "2\.0\.0, 1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2"\. . .P If the \fB\-\-patch\fR option is used, the order of preference will change to "1\.0\.4, 1\.0\.3, 1\.0\.2, 1\.1\.1, 1\.1\.0, 2\.0\.0"\. . .P If the \fB\-\-minor\fR option is used, the order of preference will change to "1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2, 2\.0\.0"\. . .P Combining the \fB\-\-strict\fR option with any of the patch level options will remove any versions beyond the scope of the patch level option, to ensure that no gem is updated that far\. . .P To continue the previous example, if both \fB\-\-patch\fR and \fB\-\-strict\fR options are used, the available versions for resolution would be "1\.0\.4, 1\.0\.3, 1\.0\.2"\. If \fB\-\-minor\fR and \fB\-\-strict\fR are used, it would be "1\.1\.1, 1\.1\.0, 1\.0\.4, 1\.0\.3, 1\.0\.2"\. . .P Gem requirements as defined in the Gemfile will still be the first determining factor for what versions are available\. If the gem requirement for \fBfoo\fR in the Gemfile is \'~> 1\.0\', that will accomplish the same thing as providing the \fB\-\-minor\fR and \fB\-\-strict\fR options\. . .SH "PATCH LEVEL EXAMPLES" Given the following gem specifications: . .IP "" 4 . .nf foo 1\.4\.3, requires: ~> bar 2\.0 foo 1\.4\.4, requires: ~> bar 2\.0 foo 1\.4\.5, requires: ~> bar 2\.1 foo 1\.5\.0, requires: ~> bar 2\.1 foo 1\.5\.1, requires: ~> bar 3\.0 bar with versions 2\.0\.3, 2\.0\.4, 2\.1\.0, 2\.1\.1, 3\.0\.0 . .fi . .IP "" 0 . .P Gemfile: . .IP "" 4 . .nf gem \'foo\' . .fi . .IP "" 0 . .P Gemfile\.lock: . .IP "" 4 . .nf foo (1\.4\.3) bar (~> 2\.0) bar (2\.0\.3) . .fi . .IP "" 0 . .P Cases: . .IP "" 4 . .nf # Command Line Result \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- 1 bundle update \-\-patch \'foo 1\.4\.5\', \'bar 2\.1\.1\' 2 bundle update \-\-patch foo \'foo 1\.4\.5\', \'bar 2\.1\.1\' 3 bundle update \-\-minor \'foo 1\.5\.1\', \'bar 3\.0\.0\' 4 bundle update \-\-minor \-\-strict \'foo 1\.5\.0\', \'bar 2\.1\.1\' 5 bundle update \-\-patch \-\-strict \'foo 1\.4\.4\', \'bar 2\.0\.4\' . .fi . .IP "" 0 . .P In case 1, bar is upgraded to 2\.1\.1, a minor version increase, because the dependency from foo 1\.4\.5 required it\. . .P In case 2, only foo is requested to be unlocked, but bar is also allowed to move because it\'s not a declared dependency in the Gemfile\. . .P In case 3, bar goes up a whole major release, because a minor increase is preferred now for foo, and when it goes to 1\.5\.1, it requires 3\.0\.0 of bar\. . .P In case 4, foo is preferred up to a minor version, but 1\.5\.1 won\'t work because the \-\-strict flag removes bar 3\.0\.0 from consideration since it\'s a major increment\. . .P In case 5, both foo and bar have any minor or major increments removed from consideration because of the \-\-strict flag, so the most they can move is up to 1\.4\.4 and 2\.0\.4\. . .SH "RECOMMENDED WORKFLOW" In general, when working with an application managed with bundler, you should use the following workflow: . .IP "\(bu" 4 After you create your Gemfile(5) for the first time, run . .IP $ bundle install . .IP "\(bu" 4 Check the resulting \fBGemfile\.lock\fR into version control . .IP $ git add Gemfile\.lock . .IP "\(bu" 4 When checking out this repository on another development machine, run . .IP $ bundle install . .IP "\(bu" 4 When checking out this repository on a deployment machine, run . .IP $ bundle install \-\-deployment . .IP "\(bu" 4 After changing the Gemfile(5) to reflect a new or update dependency, run . .IP $ bundle install . .IP "\(bu" 4 Make sure to check the updated \fBGemfile\.lock\fR into version control . .IP $ git add Gemfile\.lock . .IP "\(bu" 4 If bundle install(1) \fIbundle\-install\.1\.html\fR reports a conflict, manually update the specific gems that you changed in the Gemfile(5) . .IP $ bundle update rails thin . .IP "\(bu" 4 If you want to update all the gems to the latest possible versions that still match the gems listed in the Gemfile(5), run . .IP $ bundle update \-\-all . .IP "" 0 man/man1/bundle-open.1 0000644 00000001105 15102661023 0010436 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-OPEN" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-open\fR \- Opens the source directory for a gem in your bundle . .SH "SYNOPSIS" \fBbundle open\fR [GEM] . .SH "DESCRIPTION" Opens the source directory of the provided GEM in your editor\. . .P For this to work the \fBEDITOR\fR or \fBBUNDLER_EDITOR\fR environment variable has to be set\. . .P Example: . .IP "" 4 . .nf bundle open \'rack\' . .fi . .IP "" 0 . .P Will open the source directory for the \'rack\' gem in your bundle\. man/man1/bundle-show.1 0000644 00000001262 15102661023 0010461 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-SHOW" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem . .SH "SYNOPSIS" \fBbundle show\fR [GEM] [\-\-paths] . .SH "DESCRIPTION" Without the [GEM] option, \fBshow\fR will print a list of the names and versions of all gems that are required by your [\fBGemfile(5)\fR][Gemfile(5)], sorted by name\. . .P Calling show with [GEM] will list the exact location of that gem on your machine\. . .SH "OPTIONS" . .TP \fB\-\-paths\fR List the paths of all gems that are required by your [\fBGemfile(5)\fR][Gemfile(5)], sorted by gem name\. man/man1/rake.1 0000644 00000007215 15102661023 0007160 0 ustar 00 .Dd June 12, 2016 .Dt RAKE 1 .Os rake 11.2.2 .Sh NAME .Nm rake .Nd make-like build utility for Ruby .Sh SYNOPSIS .Nm .Op Fl f Ar rakefile .Op Ar options .Ar targets ... .Sh DESCRIPTION .Nm is a .Xr make 1 Ns -like build utility for Ruby. Tasks and dependencies are specified in standard Ruby syntax. .Sh OPTIONS .Bl -tag -width Ds .It Fl m , Fl -multitask Treat all tasks as multitasks. .It Fl B , Fl -build-all Build all prerequisites, including those which are up\-to\-date. .It Fl j , Fl -jobs Ar num_jobs Specifies the maximum number of tasks to execute in parallel (default is number of CPU cores + 4). .El .Ss Modules .Bl -tag -width Ds .It Fl I , Fl -libdir Ar libdir Include .Ar libdir in the search path for required modules. .It Fl r , Fl -require Ar module Require .Ar module before executing .Pa rakefile . .El .Ss Rakefile location .Bl -tag -width Ds .It Fl f , Fl -rakefile Ar filename Use .Ar filename as the rakefile to search for. .It Fl N , Fl -no-search , Fl -nosearch Do not search parent directories for the Rakefile. .It Fl G , Fl -no-system , Fl -nosystem Use standard project Rakefile search paths, ignore system wide rakefiles. .It Fl R , Fl -rakelib Ar rakelibdir , Fl -rakelibdir Ar rakelibdir Auto-import any .rake files in .Ar rakelibdir (default is .Sq rakelib ) .It Fl g , Fl -system Use system-wide (global) rakefiles (usually .Pa ~/.rake/*.rake ) . .El .Ss Debugging .Bl -tag -width Ds .It Fl -backtrace Ns = Ns Ar out Enable full backtrace. .Ar out can be .Dv stderr (default) or .Dv stdout . .It Fl t , Fl -trace Ns = Ns Ar out Turn on invoke/execute tracing, enable full backtrace. .Ar out can be .Dv stderr (default) or .Dv stdout . .It Fl -suppress-backtrace Ar pattern Suppress backtrace lines matching regexp .Ar pattern . Ignored if .Fl -trace is on. .It Fl -rules Trace the rules resolution. .It Fl n , Fl -dry-run Do a dry run without executing actions. .It Fl T , Fl -tasks Op Ar pattern Display the tasks (matching optional .Ar pattern ) with descriptions, then exit. .It Fl D , Fl -describe Op Ar pattern Describe the tasks (matching optional .Ar pattern ) , then exit. .It Fl W , Fl -where Op Ar pattern Describe the tasks (matching optional .Ar pattern ) , then exit. .It Fl P , Fl -prereqs Display the tasks and dependencies, then exit. .It Fl e , Fl -execute Ar code Execute some Ruby code and exit. .It Fl p , Fl -execute-print Ar code Execute some Ruby code, print the result, then exit. .It Fl E , Fl -execute-continue Ar code Execute some Ruby code, then continue with normal task processing. .El .Ss Information .Bl -tag -width Ds .It Fl v , Fl -verbose Log message to standard output. .It Fl q , Fl -quiet Do not log messages to standard output. .It Fl s , Fl -silent Like .Fl -quiet , but also suppresses the .Sq in directory announcement. .It Fl X , Fl -no-deprecation-warnings Disable the deprecation warnings. .It Fl -comments Show commented tasks only .It Fl A , Fl -all Show all tasks, even uncommented ones (in combination with .Fl T or .Fl D ) .It Fl -job-stats Op Ar level Display job statistics. If .Ar level is .Sq history , displays a complete job list. .It Fl V , Fl -version Display the program version. .It Fl h , Fl H , Fl -help Display a help message. .El .Sh SEE ALSO The complete documentation for .Nm rake has been installed at .Pa /usr/share/doc/rake-doc/html/index.html . It is also available online at .Lk https://ruby.github.io/rake . .Sh AUTHORS .An -nosplit .Nm was written by .An Jim Weirich Aq Mt jim@weirichhouse.org . .Pp This manual was created by .An Caitlin Matos Aq Mt caitlin.matos@zoho.com for the Debian project (but may be used by others). It was inspired by the manual by .An Jani Monoses Aq Mt jani@iv.ro for the Ubuntu project. man/man1/bundle-gem.1 0000644 00000012024 15102661023 0010247 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-GEM" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem . .SH "SYNOPSIS" \fBbundle gem\fR \fIGEM_NAME\fR \fIOPTIONS\fR . .SH "DESCRIPTION" Generates a directory named \fBGEM_NAME\fR with a \fBRakefile\fR, \fBGEM_NAME\.gemspec\fR, and other supporting files and directories that can be used to develop a rubygem with that name\. . .P Run \fBrake \-T\fR in the resulting project for a list of Rake tasks that can be used to test and publish the gem to rubygems\.org\. . .P The generated project skeleton can be customized with OPTIONS, as explained below\. Note that these options can also be specified via Bundler\'s global configuration file using the following names: . .IP "\(bu" 4 \fBgem\.coc\fR . .IP "\(bu" 4 \fBgem\.mit\fR . .IP "\(bu" 4 \fBgem\.test\fR . .IP "" 0 . .SH "OPTIONS" . .TP \fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\. . .TP \fB\-\-no\-exe\fR Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\. . .TP \fB\-\-coc\fR Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\. . .TP \fB\-\-no\-coc\fR Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\. . .TP \fB\-\-ext\fR Add boilerplate for C extension code to the generated project\. This behavior is disabled by default\. . .TP \fB\-\-no\-ext\fR Do not add C extension code (overrides \fB\-\-ext\fR specified in the global config)\. . .TP \fB\-\-mit\fR Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\. . .TP \fB\-\-no\-mit\fR Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\. . .TP \fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified: . .IP When Bundler is configured to generate tests, this defaults to Bundler\'s global config setting \fBgem\.test\fR\. . .IP When Bundler is configured to not generate tests, an interactive prompt will be displayed and the answer will be used for the current rubygem project\. . .IP When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\. . .TP \fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=travis\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBtravis\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified: . .IP When Bundler is configured to generate CI files, this defaults to Bundler\'s global config setting \fBgem\.ci\fR\. . .IP When Bundler is configured to not generate CI files, an interactive prompt will be displayed and the answer will be used for the current rubygem project\. . .IP When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\. . .TP \fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified: . .IP When Bundler is configured to add a linter, this defaults to Bundler\'s global config setting \fBgem\.linter\fR\. . .IP When Bundler is configured not to add a linter, an interactive prompt will be displayed and the answer will be used for the current rubygem project\. . .IP When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\. . .TP \fB\-e\fR, \fB\-\-edit[=EDITOR]\fR Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\. . .SH "SEE ALSO" . .IP "\(bu" 4 bundle config(1) \fIbundle\-config\.1\.html\fR . .IP "" 0 man/man1/bundle.1 0000644 00000007013 15102661023 0007503 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\fR \- Ruby Dependency Management . .SH "SYNOPSIS" \fBbundle\fR COMMAND [\-\-no\-color] [\-\-verbose] [ARGS] . .SH "DESCRIPTION" Bundler manages an \fBapplication\'s dependencies\fR through its entire life across many machines systematically and repeatably\. . .P See the bundler website \fIhttps://bundler\.io\fR for information on getting started, and Gemfile(5) for more information on the \fBGemfile\fR format\. . .SH "OPTIONS" . .TP \fB\-\-no\-color\fR Print all output without color . .TP \fB\-\-retry\fR, \fB\-r\fR Specify the number of times you wish to attempt network commands . .TP \fB\-\-verbose\fR, \fB\-V\fR Print out additional logging information . .SH "BUNDLE COMMANDS" We divide \fBbundle\fR subcommands into primary commands and utilities: . .SH "PRIMARY COMMANDS" . .TP \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR Install the gems specified by the \fBGemfile\fR or \fBGemfile\.lock\fR . .TP \fBbundle update(1)\fR \fIbundle\-update\.1\.html\fR Update dependencies to their latest versions . .TP \fBbundle package(1)\fR \fIbundle\-package\.1\.html\fR Package the \.gem files required by your application into the \fBvendor/cache\fR directory . .TP \fBbundle exec(1)\fR \fIbundle\-exec\.1\.html\fR Execute a script in the current bundle . .TP \fBbundle config(1)\fR \fIbundle\-config\.1\.html\fR Specify and read configuration options for Bundler . .TP \fBbundle help(1)\fR Display detailed help for each subcommand . .SH "UTILITIES" . .TP \fBbundle add(1)\fR \fIbundle\-add\.1\.html\fR Add the named gem to the Gemfile and run \fBbundle install\fR . .TP \fBbundle binstubs(1)\fR \fIbundle\-binstubs\.1\.html\fR Generate binstubs for executables in a gem . .TP \fBbundle check(1)\fR \fIbundle\-check\.1\.html\fR Determine whether the requirements for your application are installed and available to Bundler . .TP \fBbundle show(1)\fR \fIbundle\-show\.1\.html\fR Show the source location of a particular gem in the bundle . .TP \fBbundle outdated(1)\fR \fIbundle\-outdated\.1\.html\fR Show all of the outdated gems in the current bundle . .TP \fBbundle console(1)\fR Start an IRB session in the current bundle . .TP \fBbundle open(1)\fR \fIbundle\-open\.1\.html\fR Open an installed gem in the editor . .TP \fBbundle lock(1)\fR \fIbundle\-lock\.1\.html\fR Generate a lockfile for your dependencies . .TP \fBbundle viz(1)\fR \fIbundle\-viz\.1\.html\fR Generate a visual representation of your dependencies . .TP \fBbundle init(1)\fR \fIbundle\-init\.1\.html\fR Generate a simple \fBGemfile\fR, placed in the current directory . .TP \fBbundle gem(1)\fR \fIbundle\-gem\.1\.html\fR Create a simple gem, suitable for development with Bundler . .TP \fBbundle platform(1)\fR \fIbundle\-platform\.1\.html\fR Display platform compatibility information . .TP \fBbundle clean(1)\fR \fIbundle\-clean\.1\.html\fR Clean up unused gems in your Bundler directory . .TP \fBbundle doctor(1)\fR \fIbundle\-doctor\.1\.html\fR Display warnings about common problems . .TP \fBbundle remove(1)\fR \fIbundle\-remove\.1\.html\fR Removes gems from the Gemfile . .SH "PLUGINS" When running a command that isn\'t listed in PRIMARY COMMANDS or UTILITIES, Bundler will try to find an executable on your path named \fBbundler\-<command>\fR and execute it, passing down any extra arguments to it\. . .SH "OBSOLETE" These commands are obsolete and should no longer be used: . .IP "\(bu" 4 \fBbundle cache(1)\fR . .IP "\(bu" 4 \fBbundle show(1)\fR . .IP "" 0 man/man1/ruby.1 0000644 00000045207 15102661023 0007222 0 ustar 00 .\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>. .Dd April 14, 2018 .Dt RUBY \&1 "Ruby Programmer's Reference Guide" .Os UNIX .Sh NAME .Nm ruby .Nd Interpreted object-oriented scripting language .Sh SYNOPSIS .Nm .Op Fl -copyright .Op Fl -version .Op Fl SUacdlnpswvy .Op Fl 0 Ns Op Ar octal .Op Fl C Ar directory .Op Fl E Ar external Ns Op : Ns Ar internal .Op Fl F Ns Op Ar pattern .Op Fl I Ar directory .Op Fl K Ns Op Ar c .Op Fl T Ns Op Ar level .Op Fl W Ns Op Ar level .Op Fl e Ar command .Op Fl i Ns Op Ar extension .Op Fl r Ar library .Op Fl x Ns Op Ar directory .Op Fl - Ns Bro Cm enable Ns | Ns Cm disable Brc Ns - Ns Ar FEATURE .Op Fl -dump Ns = Ns Ar target .Op Fl -verbose .Op Fl - .Op Ar program_file .Op Ar argument ... .Sh DESCRIPTION Ruby is an interpreted scripting language for quick and easy object-oriented programming. It has many features to process text files and to do system management tasks (like in Perl). It is simple, straight-forward, and extensible. .Pp If you want a language for easy object-oriented programming, or you don't like the Perl ugliness, or you do like the concept of LISP, but don't like too many parentheses, Ruby might be your language of choice. .Sh FEATURES Ruby's features are as follows: .Bl -tag -width 6n .It Sy "Interpretive" Ruby is an interpreted language, so you don't have to recompile programs written in Ruby to execute them. .Pp .It Sy "Variables have no type (dynamic typing)" Variables in Ruby can contain data of any type. You don't have to worry about variable typing. Consequently, it has a weaker compile time check. .Pp .It Sy "No declaration needed" You can use variables in your Ruby programs without any declarations. Variable names denote their scope - global, class, instance, or local. .Pp .It Sy "Simple syntax" Ruby has a simple syntax influenced slightly from Eiffel. .Pp .It Sy "No user-level memory management" Ruby has automatic memory management. Objects no longer referenced from anywhere are automatically collected by the garbage collector built into the interpreter. .Pp .It Sy "Everything is an object" Ruby is a purely object-oriented language, and was so since its creation. Even such basic data as integers are seen as objects. .Pp .It Sy "Class, inheritance, and methods" Being an object-oriented language, Ruby naturally has basic features like classes, inheritance, and methods. .Pp .It Sy "Singleton methods" Ruby has the ability to define methods for certain objects. For example, you can define a press-button action for certain widget by defining a singleton method for the button. Or, you can make up your own prototype based object system using singleton methods, if you want to. .Pp .It Sy "Mix-in by modules" Ruby intentionally does not have the multiple inheritance as it is a source of confusion. Instead, Ruby has the ability to share implementations across the inheritance tree. This is often called a .Sq Mix-in . .Pp .It Sy "Iterators" Ruby has iterators for loop abstraction. .Pp .It Sy "Closures" In Ruby, you can objectify the procedure. .Pp .It Sy "Text processing and regular expressions" Ruby has a bunch of text processing features like in Perl. .Pp .It Sy "M17N, character set independent" Ruby supports multilingualized programming. Easy to process texts written in many different natural languages and encoded in many different character encodings, without dependence on Unicode. .Pp .It Sy "Bignums" With built-in bignums, you can for example calculate factorial(400). .Pp .It Sy "Reflection and domain specific languages" Class is also an instance of the Class class. Definition of classes and methods is an expression just as 1+1 is. So your programs can even write and modify programs. Thus you can write your application in your own programming language on top of Ruby. .Pp .It Sy "Exception handling" As in Java(tm). .Pp .It Sy "Direct access to the OS" Ruby can use most .Ux system calls, often used in system programming. .Pp .It Sy "Dynamic loading" On most .Ux systems, you can load object files into the Ruby interpreter on-the-fly. .It Sy "Rich libraries" In addition to the .Dq builtin libraries and .Dq standard libraries that are bundled with Ruby, a vast amount of third-party libraries .Pq Dq gems are available via the package management system called .Sq RubyGems , namely the .Xr gem 1 command. Visit RubyGems.org .Pq Lk https://rubygems.org/ to find the gems you need, and explore GitHub .Pq Lk https://github.com/ to see how they are being developed and used. .El .Pp .Sh OPTIONS The Ruby interpreter accepts the following command-line options (switches). They are quite similar to those of .Xr perl 1 . .Bl -tag -width "1234567890123" -compact .Pp .It Fl -copyright Prints the copyright notice, and quits immediately without running any script. .Pp .It Fl -version Prints the version of the Ruby interpreter, and quits immediately without running any script. .Pp .It Fl 0 Ns Op Ar octal (The digit .Dq zero . ) Specifies the input record separator .Pf ( Li "$/" ) as an octal number. If no digit is given, the null character is taken as the separator. Other switches may follow the digits. .Fl 00 turns Ruby into paragraph mode. .Fl 0777 makes Ruby read whole file at once as a single string since there is no legal character with that value. .Pp .It Fl C Ar directory .It Fl X Ar directory Causes Ruby to switch to the directory. .Pp .It Fl E Ar external Ns Op : Ns Ar internal .It Fl -encoding Ar external Ns Op : Ns Ar internal Specifies the default value(s) for external encodings and internal encoding. Values should be separated with colon (:). .Pp You can omit the one for internal encodings, then the value .Pf ( Li "Encoding.default_internal" ) will be nil. .Pp .It Fl -external-encoding Ns = Ns Ar encoding .It Fl -internal-encoding Ns = Ns Ar encoding Specify the default external or internal character encoding .Pp .It Fl F Ar pattern Specifies input field separator .Pf ( Li "$;" ) . .Pp .It Fl I Ar directory Used to tell Ruby where to load the library scripts. Directory path will be added to the load-path variable .Pf ( Li "$:" ) . .Pp .It Fl K Ar kcode Specifies KANJI (Japanese) encoding. The default value for script encodings .Pf ( Li "__ENCODING__" ) and external encodings ( Li "Encoding.default_external" ) will be the specified one. .Ar kcode can be one of .Bl -hang -offset indent .It Sy e EUC-JP .Pp .It Sy s Windows-31J (CP932) .Pp .It Sy u UTF-8 .Pp .It Sy n ASCII-8BIT (BINARY) .El .Pp .It Fl S Makes Ruby use the .Ev PATH environment variable to search for script, unless its name begins with a slash. This is used to emulate .Li #! on machines that don't support it, in the following manner: .Bd -literal -offset indent #! /usr/local/bin/ruby # This line makes the next one a comment in Ruby \e exec /usr/local/bin/ruby -S $0 $* .Ed .Pp On some systems .Li "$0" does not always contain the full pathname, so you need the .Fl S switch to tell Ruby to search for the script if necessary (to handle embedded spaces and such). A better construct than .Li "$*" would be .Li ${1+"$@"} , but it does not work if the script is being interpreted by .Xr csh 1 . .Pp .It Fl T Ns Op Ar level=1 Turns on taint checks at the specified level (default 1). .Pp .It Fl U Sets the default value for internal encodings .Pf ( Li "Encoding.default_internal" ) to UTF-8. .Pp .It Fl W Ns Op Ar level=2 Turns on verbose mode at the specified level without printing the version message at the beginning. The level can be; .Bl -hang -offset indent .It Sy 0 Verbose mode is "silence". It sets the .Li "$VERBOSE" to nil. .Pp .It Sy 1 Verbose mode is "medium". It sets the .Li "$VERBOSE" to false. .Pp .It Sy 2 (default) Verbose mode is "verbose". It sets the .Li "$VERBOSE" to true. .Fl W Ns 2 is same as .Fl w . .El .Pp .It Fl a Turns on auto-split mode when used with .Fl n or .Fl p . In auto-split mode, Ruby executes .Dl $F = $_.split at beginning of each loop. .Pp .It Fl c Causes Ruby to check the syntax of the script and exit without executing. If there are no syntax errors, Ruby will print .Dq Syntax OK to the standard output. .Pp .It Fl d .It Fl -debug Turns on debug mode. .Li "$DEBUG" will be set to true. .Pp .It Fl e Ar command Specifies script from command-line while telling Ruby not to search the rest of the arguments for a script file name. .Pp .It Fl h .It Fl -help Prints a summary of the options. .Pp .It Fl i Ar extension Specifies in-place-edit mode. The extension, if specified, is added to old file name to make a backup copy. For example: .Bd -literal -offset indent % echo matz > /tmp/junk % cat /tmp/junk matz % ruby -p -i.bak -e '$_.upcase!' /tmp/junk % cat /tmp/junk MATZ % cat /tmp/junk.bak matz .Ed .Pp .It Fl l (The lowercase letter .Dq ell . ) Enables automatic line-ending processing, which means to firstly set .Li "$\e" to the value of .Li "$/" , and secondly chops every line read using .Li chomp! . .Pp .It Fl n Causes Ruby to assume the following loop around your script, which makes it iterate over file name arguments somewhat like .Nm sed .Fl n or .Nm awk . .Bd -literal -offset indent while gets ... end .Ed .Pp .It Fl p Acts mostly same as -n switch, but print the value of variable .Li "$_" at the each end of the loop. For example: .Bd -literal -offset indent % echo matz | ruby -p -e '$_.tr! "a-z", "A-Z"' MATZ .Ed .Pp .It Fl r Ar library Causes Ruby to load the library using require. It is useful when using .Fl n or .Fl p . .Pp .It Fl s Enables some switch parsing for switches after script name but before any file name arguments (or before a .Fl - ) . Any switches found there are removed from .Li ARGV and set the corresponding variable in the script. For example: .Bd -literal -offset indent #! /usr/local/bin/ruby -s # prints "true" if invoked with `-xyz' switch. print "true\en" if $xyz .Ed .Pp .It Fl v Enables verbose mode. Ruby will print its version at the beginning and set the variable .Li "$VERBOSE" to true. Some methods print extra messages if this variable is true. If this switch is given, and no other switches are present, Ruby quits after printing its version. .Pp .It Fl w Enables verbose mode without printing version message at the beginning. It sets the .Li "$VERBOSE" variable to true. .Pp .It Fl x Ns Op Ar directory Tells Ruby that the script is embedded in a message. Leading garbage will be discarded until the first line that starts with .Dq #! and contains the string, .Dq ruby . Any meaningful switches on that line will be applied. The end of the script must be specified with either .Li EOF , .Li "^D" ( Li "control-D" ) , .Li "^Z" ( Li "control-Z" ) , or the reserved word .Li __END__ . If the directory name is specified, Ruby will switch to that directory before executing script. .Pp .It Fl y .It Fl -yydebug DO NOT USE. .Pp Turns on compiler debug mode. Ruby will print a bunch of internal state messages during compilation. Only specify this switch you are going to debug the Ruby interpreter. .Pp .It Fl -disable- Ns Ar FEATURE .It Fl -enable- Ns Ar FEATURE Disables (or enables) the specified .Ar FEATURE . .Bl -tag -width "--disable-rubyopt" -compact .It Fl -disable-gems .It Fl -enable-gems Disables (or enables) RubyGems libraries. By default, Ruby will load the latest version of each installed gem. The .Li Gem constant is true if RubyGems is enabled, false if otherwise. .Pp .It Fl -disable-rubyopt .It Fl -enable-rubyopt Ignores (or considers) the .Ev RUBYOPT environment variable. By default, Ruby considers the variable. .Pp .It Fl -disable-all .It Fl -enable-all Disables (or enables) all features. .Pp .El .Pp .It Fl -dump Ns = Ns Ar target Dump some information. .Pp Prints the specified target. .Ar target can be one of; .Bl -hang -offset indent .It Sy version version description same as .Fl -version .It Sy usage brief usage message same as .Fl h .It Sy help Show long help message same as .Fl -help .It Sy syntax check of syntax same as .Fl c .Fl -yydebug .It Sy yydebug compiler debug mode, same as .Fl -yydebug .Pp Only specify this switch if you are going to debug the Ruby interpreter. .It Sy parsetree .It Sy parsetree_with_comment AST nodes tree .Pp Only specify this switch if you are going to debug the Ruby interpreter. .It Sy insns disassembled instructions .Pp Only specify this switch if you are going to debug the Ruby interpreter. .El .Pp .It Fl -verbose Enables verbose mode without printing version message at the beginning. It sets the .Li "$VERBOSE" variable to true. If this switch is given, and no script arguments (script file or .Fl e options) are present, Ruby quits immediately. .El .Pp .Sh ENVIRONMENT .Bl -tag -width "RUBYSHELL" -compact .It Ev RUBYLIB A colon-separated list of directories that are added to Ruby's library load path .Pf ( Li "$:" ) . Directories from this environment variable are searched before the standard load path is searched. .Pp e.g.: .Dl RUBYLIB="$HOME/lib/ruby:$HOME/lib/rubyext" .Pp .It Ev RUBYOPT Additional Ruby options. .Pp e.g. .Dl RUBYOPT="-w -Ke" .Pp Note that RUBYOPT can contain only .Fl d , Fl E , Fl I , Fl K , Fl r , Fl T , Fl U , Fl v , Fl w , Fl W, Fl -debug , .Fl -disable- Ns Ar FEATURE and .Fl -enable- Ns Ar FEATURE . .Pp .It Ev RUBYPATH A colon-separated list of directories that Ruby searches for Ruby programs when the .Fl S flag is specified. This variable precedes the .Ev PATH environment variable. .Pp .It Ev RUBYSHELL The path to the system shell command. This environment variable is enabled for only mswin32, mingw32, and OS/2 platforms. If this variable is not defined, Ruby refers to .Ev COMSPEC . .Pp .It Ev PATH Ruby refers to the .Ev PATH environment variable on calling Kernel#system. .El .Pp And Ruby depends on some RubyGems related environment variables unless RubyGems is disabled. See the help of .Xr gem 1 as below. .Bd -literal -offset indent % gem help .Ed .Pp .Sh GC ENVIRONMENT The Ruby garbage collector (GC) tracks objects in fixed-sized slots, but each object may have auxiliary memory allocations handled by the malloc family of C standard library calls ( .Xr malloc 3 , .Xr calloc 3 , and .Xr realloc 3 ) . In this documentatation, the "heap" refers to the Ruby object heap of fixed-sized slots, while "malloc" refers to auxiliary allocations commonly referred to as the "process heap". Thus there are at least two possible ways to trigger GC: .Bl -hang -offset indent .It Sy 1 Reaching the object limit. .It Sy 2 Reaching the malloc limit. .Pp .El In Ruby 2.1, the generational GC was introduced and the limits are divided into young and old generations, providing two additional ways to trigger a GC: .Bl -hang -offset indent .It Sy 3 Reaching the old object limit. .It Sy 4 Reaching the old malloc limit. .El .Pp There are currently 4 possible areas where the GC may be tuned by the following 11 environment variables: .Bl -hang -compact -width "RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR" .It Ev RUBY_GC_HEAP_INIT_SLOTS Initial allocation slots. Introduced in Ruby 2.1, default: 10000. .Pp .It Ev RUBY_GC_HEAP_FREE_SLOTS Prepare at least this amount of slots after GC. Allocate this number slots if there are not enough slots. Introduced in Ruby 2.1, default: 4096 .Pp .It Ev RUBY_GC_HEAP_GROWTH_FACTOR Increase allocation rate of heap slots by this factor. Introduced in Ruby 2.1, default: 1.8, minimum: 1.0 (no growth) .Pp .It Ev RUBY_GC_HEAP_GROWTH_MAX_SLOTS Allocation rate is limited to this number of slots, preventing excessive allocation due to RUBY_GC_HEAP_GROWTH_FACTOR. Introduced in Ruby 2.1, default: 0 (no limit) .Pp .It Ev RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR Perform a full GC when the number of old objects is more than R * N, where R is this factor and N is the number of old objects after the last full GC. Introduced in Ruby 2.1.1, default: 2.0 .Pp .It Ev RUBY_GC_MALLOC_LIMIT The initial limit of young generation allocation from the malloc-family. GC will start when this limit is reached. Default: 16MB .Pp .It Ev RUBY_GC_MALLOC_LIMIT_MAX The maximum limit of young generation allocation from malloc before GC starts. Prevents excessive malloc growth due to RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR. Introduced in Ruby 2.1, default: 32MB. .Pp .It Ev RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR Increases the limit of young generation malloc calls, reducing GC frequency but increasing malloc growth until RUBY_GC_MALLOC_LIMIT_MAX is reached. Introduced in Ruby 2.1, default: 1.4, minimum: 1.0 (no growth) .Pp .It Ev RUBY_GC_OLDMALLOC_LIMIT The initial limit of old generation allocation from malloc, a full GC will start when this limit is reached. Introduced in Ruby 2.1, default: 16MB .Pp .It Ev RUBY_GC_OLDMALLOC_LIMIT_MAX The maximum limit of old generation allocation from malloc before a full GC starts. Prevents excessive malloc growth due to RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR. Introduced in Ruby 2.1, default: 128MB .Pp .It Ev RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR Increases the limit of old generation malloc allocation, reducing full GC frequency but increasing malloc growth until RUBY_GC_OLDMALLOC_LIMIT_MAX is reached. Introduced in Ruby 2.1, default: 1.2, minimum: 1.0 (no growth) .Pp .El .Sh STACK SIZE ENVIRONMENT Stack size environment variables are implementation-dependent and subject to change with different versions of Ruby. The VM stack is used for pure-Ruby code and managed by the virtual machine. Machine stack is used by the operating system and its usage is dependent on C extensions as well as C compiler options. Using lower values for these may allow applications to keep more Fibers or Threads running; but increases the chance of SystemStackError exceptions and segmentation faults (SIGSEGV). These environment variables are available since Ruby 2.0.0. All values are specified in bytes. .Pp .Bl -hang -compact -width "RUBY_THREAD_MACHINE_STACK_SIZE" .It Ev RUBY_THREAD_VM_STACK_SIZE VM stack size used at thread creation. default: 131072 (32-bit CPU) or 262144 (64-bit) .Pp .It Ev RUBY_THREAD_MACHINE_STACK_SIZE Machine stack size used at thread creation. default: 524288 or 1048575 .Pp .It Ev RUBY_FIBER_VM_STACK_SIZE VM stack size used at fiber creation. default: 65536 or 131072 .Pp .It Ev RUBY_FIBER_MACHINE_STACK_SIZE Machine stack size used at fiber creation. default: 262144 or 524288 .Pp .El .Sh SEE ALSO .Bl -hang -compact -width "https://www.ruby-toolbox.com/" .It Lk https://www.ruby-lang.org/ The official web site. .It Lk https://www.ruby-toolbox.com/ Comprehensive catalog of Ruby libraries. .El .Pp .Sh REPORTING BUGS .Bl -bullet .It Security vulnerabilities should be reported via an email to .Mt security@ruby-lang.org . Reported problems will be published after being fixed. .Pp .It Other bugs and feature requests can be reported via the Ruby Issue Tracking System .Pq Lk https://bugs.ruby-lang.org/ . Do not report security vulnerabilities via this system because it publishes the vulnerabilities immediately. .El .Sh AUTHORS Ruby is designed and implemented by .An Yukihiro Matsumoto Aq matz@netlab.jp . .Pp See .Aq Lk https://bugs.ruby-lang.org/projects/ruby/wiki/Contributors for contributors to Ruby. man/man1/bundle-platform.1 0000644 00000002474 15102661023 0011333 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-PLATFORM" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-platform\fR \- Displays platform compatibility information . .SH "SYNOPSIS" \fBbundle platform\fR [\-\-ruby] . .SH "DESCRIPTION" \fBplatform\fR will display information from your Gemfile, Gemfile\.lock, and Ruby VM about your platform\. . .P For instance, using this Gemfile(5): . .IP "" 4 . .nf source "https://rubygems\.org" ruby "1\.9\.3" gem "rack" . .fi . .IP "" 0 . .P If you run \fBbundle platform\fR on Ruby 1\.9\.3, it will display the following output: . .IP "" 4 . .nf Your platform is: x86_64\-linux Your app has gems that work on these platforms: * ruby Your Gemfile specifies a Ruby version requirement: * ruby 1\.9\.3 Your current platform satisfies the Ruby version requirement\. . .fi . .IP "" 0 . .P \fBplatform\fR will list all the platforms in your \fBGemfile\.lock\fR as well as the \fBruby\fR directive if applicable from your Gemfile(5)\. It will also let you know if the \fBruby\fR directive requirement has been met\. If \fBruby\fR directive doesn\'t match the running Ruby VM, it will tell you what part does not\. . .SH "OPTIONS" . .TP \fB\-\-ruby\fR It will display the ruby directive information, so you don\'t have to parse it from the Gemfile(5)\. man/man1/bundle-remove.1 0000644 00000001521 15102661023 0010774 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-REMOVE" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-remove\fR \- Removes gems from the Gemfile . .SH "SYNOPSIS" \fBbundle remove [GEM [GEM \.\.\.]] [\-\-install]\fR . .SH "DESCRIPTION" Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid\. If a gem cannot be removed, a warning is printed\. If a gem is already absent from the Gemfile, and error is raised\. . .SH "OPTIONS" . .TP \fB\-\-install\fR Runs \fBbundle install\fR after the given gems have been removed from the Gemfile, which ensures that both the lockfile and the installed gems on disk are also updated to remove the given gem(s)\. . .P Example: . .P bundle remove rails . .P bundle remove rails rack . .P bundle remove rails rack \-\-install man/man1/erb.1 0000644 00000006376 15102661023 0007015 0 ustar 00 .\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>. .Dd December 16, 2018 .Dt ERB \&1 "Ruby Programmer's Reference Guide" .Os UNIX .Sh NAME .Nm erb .Nd Ruby Templating .Sh SYNOPSIS .Nm .Op Fl -version .Op Fl UPdnvx .Op Fl E Ar ext Ns Op Ns : Ns int .Op Fl S Ar level .Op Fl T Ar mode .Op Fl r Ar library .Op Fl - .Op file ... .Pp .Sh DESCRIPTION .Nm is a command line front-end for .Li "ERB" library, which is an implementation of eRuby. .Pp ERB provides an easy to use but powerful templating system for Ruby. Using ERB, actual Ruby code can be added to any plain text document for the purposes of generating document information details and/or flow control. .Pp .Nm is a part of .Nm Ruby . .Pp .Sh OPTIONS .Bl -tag -width "1234567890123" -compact .Pp .It Fl -version Prints the version of .Nm . .Pp .It Fl E Ar external Ns Op : Ns Ar internal .It Fl -encoding Ar external Ns Op : Ns Ar internal Specifies the default value(s) for external encodings and internal encoding. Values should be separated with colon (:). .Pp You can omit the one for internal encodings, then the value .Pf ( Li "Encoding.default_internal" ) will be nil. .Pp .It Fl P Disables ruby code evaluation for lines beginning with .Li "%" . .Pp .It Fl S Ar level Specifies the safe level in which eRuby script will run. .Pp .It Fl T Ar mode Specifies trim mode (default 0). .Ar mode can be one of .Bl -hang -offset indent .It Sy 0 EOL remains after the embedded ruby script is evaluated. .Pp .It Sy 1 EOL is removed if the line ends with .Li "%>" . .Pp .It Sy 2 EOL is removed if the line starts with .Li "<%" and ends with .Li "%>" . .Pp .It Sy - EOL is removed if the line ends with .Li "-%>" . And leading whitespaces are removed if the erb directive starts with .Li "<%-" . .Pp .El .It Fl r Load a library .Pp .It Fl U can be one of Sets the default value for internal encodings .Pf ( Li "Encoding.default_internal" ) to UTF-8. .Pp .It Fl d .It Fl -debug Turns on debug mode. .Li "$DEBUG" will be set to true. .Pp .It Fl h .It Fl -help Prints a summary of the options. .Pp .It Fl n Used with .Fl x . Prepends the line number to each line in the output. .Pp .It Fl v Enables verbose mode. .Li "$VERBOSE" will be set to true. .Pp .It Fl x Converts the eRuby script into Ruby script and prints it without line numbers. .Pp .El .Pp .Sh EXAMPLES Here is an eRuby script .Bd -literal -offset indent <?xml version="1.0" ?> <% require 'prime' -%> <erb-example> <calc><%= 1+1 %></calc> <var><%= __FILE__ %></var> <library><%= Prime.each(10).to_a.join(", ") %></library> </erb-example> .Ed .Pp Command .Dl "% erb -T - example.erb" prints .Bd -literal -offset indent <?xml version="1.0" ?> <erb-example> <calc>2</calc> <var>example.erb</var> <library>2, 3, 5, 7</library> </erb-example> .Ed .Pp .Sh SEE ALSO .Xr ruby 1 . .Pp And see .Xr ri 1 documentation for .Li "ERB" class. .Pp .Sh REPORTING BUGS .Bl -bullet .It Security vulnerabilities should be reported via an email to .Mt security@ruby-lang.org . Reported problems will be published after being fixed. .Pp .It Other bugs and feature requests can be reported via the Ruby Issue Tracking System .Pq Lk https://bugs.ruby-lang.org/ . Do not report security vulnerabilities via this system because it publishes the vulnerabilities immediately. .El .Sh AUTHORS Written by Masatoshi SEKI. man/man1/bundle-inject.1 0000644 00000001342 15102661023 0010754 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-INJECT" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile . .SH "SYNOPSIS" \fBbundle inject\fR [GEM] [VERSION] . .SH "DESCRIPTION" Adds the named gem(s) with their version requirements to the resolved [\fBGemfile(5)\fR][Gemfile(5)]\. . .P This command will add the gem to both your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock if it isn\'t listed yet\. . .P Example: . .IP "" 4 . .nf bundle install bundle inject \'rack\' \'> 0\' . .fi . .IP "" 0 . .P This will inject the \'rack\' gem with a version greater than 0 in your [\fBGemfile(5)\fR][Gemfile(5)] and Gemfile\.lock man/man1/bundle-pristine.1 0000644 00000003216 15102661023 0011337 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-PRISTINE" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition . .SH "SYNOPSIS" \fBbundle pristine\fR . .SH "DESCRIPTION" \fBpristine\fR restores the installed gems in the bundle to their pristine condition using the local gem cache from RubyGems\. For git gems, a forced checkout will be performed\. . .P For further explanation, \fBbundle pristine\fR ignores unpacked files on disk\. In other words, this command utilizes the local \fB\.gem\fR cache or the gem\'s git repository as if one were installing from scratch\. . .P Note: the Bundler gem cannot be restored to its original state with \fBpristine\fR\. One also cannot use \fBbundle pristine\fR on gems with a \'path\' option in the Gemfile, because bundler has no original copy it can restore from\. . .P When is it practical to use \fBbundle pristine\fR? . .P It comes in handy when a developer is debugging a gem\. \fBbundle pristine\fR is a great way to get rid of experimental changes to a gem that one may not want\. . .P Why use \fBbundle pristine\fR over \fBgem pristine \-\-all\fR? . .P Both commands are very similar\. For context: \fBbundle pristine\fR, without arguments, cleans all gems from the lockfile\. Meanwhile, \fBgem pristine \-\-all\fR cleans all installed gems for that Ruby version\. . .P If a developer forgets which gems in their project they might have been debugging, the Rubygems \fBgem pristine [GEMNAME]\fR command may be inconvenient\. One can avoid waiting for \fBgem pristine \-\-all\fR, and instead run \fBbundle pristine\fR\. man/man1/bundle-binstubs.1 0000644 00000003120 15102661023 0011325 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-BINSTUBS" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-binstubs\fR \- Install the binstubs of the listed gems . .SH "SYNOPSIS" \fBbundle binstubs\fR \fIGEM_NAME\fR [\-\-force] [\-\-path PATH] [\-\-standalone] . .SH "DESCRIPTION" Binstubs are scripts that wrap around executables\. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it into \fBbin/\fR\. Binstubs are a shortcut\-or alternative\- to always using \fBbundle exec\fR\. This gives you a file that can be run directly, and one that will always run the correct gem version used by the application\. . .P For example, if you run \fBbundle binstubs rspec\-core\fR, Bundler will create the file \fBbin/rspec\fR\. That file will contain enough code to load Bundler, tell it to load the bundled gems, and then run rspec\. . .P This command generates binstubs for executables in \fBGEM_NAME\fR\. Binstubs are put into \fBbin\fR, or the \fB\-\-path\fR directory if one has been set\. Calling binstubs with [GEM [GEM]] will create binstubs for all given gems\. . .SH "OPTIONS" . .TP \fB\-\-force\fR Overwrite existing binstubs if they exist\. . .TP \fB\-\-path\fR The location to install the specified binstubs to\. This defaults to \fBbin\fR\. . .TP \fB\-\-standalone\fR Makes binstubs that can work without depending on Rubygems or Bundler at runtime\. . .TP \fB\-\-shebang\fR Specify a different shebang executable name than the default (default \'ruby\') . .TP \fB\-\-all\fR Create binstubs for all gems in the bundle\. man/man1/bundle-check.1 0000644 00000001706 15102661023 0010561 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-CHECK" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems . .SH "SYNOPSIS" \fBbundle check\fR [\-\-dry\-run] [\-\-gemfile=FILE] [\-\-path=PATH] . .SH "DESCRIPTION" \fBcheck\fR searches the local machine for each of the gems requested in the Gemfile\. If all gems are found, Bundler prints a success message and exits with a status of 0\. . .P If not, the first missing gem is listed and Bundler exits status 1\. . .SH "OPTIONS" . .TP \fB\-\-dry\-run\fR Locks the [\fBGemfile(5)\fR][Gemfile(5)] before running the command\. . .TP \fB\-\-gemfile\fR Use the specified gemfile instead of the [\fBGemfile(5)\fR][Gemfile(5)]\. . .TP \fB\-\-path\fR Specify a different path than the system default (\fB$BUNDLE_PATH\fR or \fB$GEM_HOME\fR)\. Bundler will remember this value for future installs on this machine\. man/man1/bundle-config.1 0000644 00000051661 15102661023 0010756 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-CONFIG" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-config\fR \- Set bundler configuration options . .SH "SYNOPSIS" \fBbundle config\fR [list|get|set|unset] [\fIname\fR [\fIvalue\fR]] . .SH "DESCRIPTION" This command allows you to interact with Bundler\'s configuration system\. . .P Bundler loads configuration settings in this order: . .IP "1." 4 Local config (\fB<project_root>/\.bundle/config\fR or \fB$BUNDLE_APP_CONFIG/config\fR) . .IP "2." 4 Environmental variables (\fBENV\fR) . .IP "3." 4 Global config (\fB~/\.bundle/config\fR) . .IP "4." 4 Bundler default config . .IP "" 0 . .P Executing \fBbundle config list\fR with will print a list of all bundler configuration for the current bundle, and where that configuration was set\. . .P Executing \fBbundle config get <name>\fR will print the value of that configuration setting, and where it was set\. . .P Executing \fBbundle config set <name> <value>\fR will set that configuration to the value specified for all bundles executed as the current user\. The configuration will be stored in \fB~/\.bundle/config\fR\. If \fIname\fR already is set, \fIname\fR will be overridden and user will be warned\. . .P Executing \fBbundle config set \-\-global <name> <value>\fR works the same as above\. . .P Executing \fBbundle config set \-\-local <name> <value>\fR will set that configuration in the directory for the local application\. The configuration will be stored in \fB<project_root>/\.bundle/config\fR\. If \fBBUNDLE_APP_CONFIG\fR is set, the configuration will be stored in \fB$BUNDLE_APP_CONFIG/config\fR\. . .P Executing \fBbundle config unset <name>\fR will delete the configuration in both local and global sources\. . .P Executing \fBbundle config unset \-\-global <name>\fR will delete the configuration only from the user configuration\. . .P Executing \fBbundle config unset \-\-local <name> <value>\fR will delete the configuration only from the local application\. . .P Executing bundle with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\. . .SH "REMEMBERING OPTIONS" Flags passed to \fBbundle install\fR or the Bundler runtime, such as \fB\-\-path foo\fR or \fB\-\-without production\fR, are remembered between commands and saved to your local application\'s configuration (normally, \fB\./\.bundle/config\fR)\. . .P However, this will be changed in bundler 3, so it\'s better not to rely on this behavior\. If these options must be remembered, it\'s better to set them using \fBbundle config\fR (e\.g\., \fBbundle config set \-\-local path foo\fR)\. . .P The options that can be configured are: . .TP \fBbin\fR Creates a directory (defaults to \fB~/bin\fR) and place any executables from the gem there\. These executables run in Bundler\'s context\. If used, you might add this directory to your environment\'s \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\. . .TP \fBdeployment\fR In deployment mode, Bundler will \'roll\-out\' the bundle for \fBproduction\fR use\. Please check carefully if you want to have this option enabled in \fBdevelopment\fR or \fBtest\fR environments\. . .TP \fBpath\fR The location to install the specified gems to\. This defaults to Rubygems\' setting\. Bundler shares this location with Rubygems, \fBgem install \.\.\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \.\.\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\. . .TP \fBwithout\fR A space\-separated list of groups referencing gems to skip during installation\. . .TP \fBwith\fR A space\-separated list of groups referencing gems to include during installation\. . .SH "BUILD OPTIONS" You can use \fBbundle config\fR to give Bundler the flags to pass to the gem installer every time bundler tries to install a particular gem\. . .P A very common example, the \fBmysql\fR gem, requires Snow Leopard users to pass configuration flags to \fBgem install\fR to specify where to find the \fBmysql_config\fR executable\. . .IP "" 4 . .nf gem install mysql \-\- \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config . .fi . .IP "" 0 . .P Since the specific location of that executable can change from machine to machine, you can specify these flags on a per\-machine basis\. . .IP "" 4 . .nf bundle config set \-\-global build\.mysql \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config . .fi . .IP "" 0 . .P After running this command, every time bundler needs to install the \fBmysql\fR gem, it will pass along the flags you specified\. . .SH "CONFIGURATION KEYS" Configuration keys in bundler have two forms: the canonical form and the environment variable form\. . .P For instance, passing the \fB\-\-without\fR flag to bundle install(1) \fIbundle\-install\.1\.html\fR prevents Bundler from installing certain groups specified in the Gemfile(5)\. Bundler persists this value in \fBapp/\.bundle/config\fR so that calls to \fBBundler\.setup\fR do not try to find gems from the \fBGemfile\fR that you didn\'t install\. Additionally, subsequent calls to bundle install(1) \fIbundle\-install\.1\.html\fR remember this setting and skip those groups\. . .P The canonical form of this configuration is \fB"without"\fR\. To convert the canonical form to the environment variable form, capitalize it, and prepend \fBBUNDLE_\fR\. The environment variable form of \fB"without"\fR is \fBBUNDLE_WITHOUT\fR\. . .P Any periods in the configuration keys must be replaced with two underscores when setting it via environment variables\. The configuration key \fBlocal\.rack\fR becomes the environment variable \fBBUNDLE_LOCAL__RACK\fR\. . .SH "LIST OF AVAILABLE KEYS" The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\. . .IP "\(bu" 4 \fBallow_deployment_source_credential_changes\fR (\fBBUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES\fR): When in deployment mode, allow changing the credentials to a gem\'s source\. Ex: \fBhttps://some\.host\.com/gems/path/\fR \-> \fBhttps://user_name:password@some\.host\.com/gems/path\fR . .IP "\(bu" 4 \fBallow_offline_install\fR (\fBBUNDLE_ALLOW_OFFLINE_INSTALL\fR): Allow Bundler to use cached data when installing without network access\. . .IP "\(bu" 4 \fBauto_clean_without_path\fR (\fBBUNDLE_AUTO_CLEAN_WITHOUT_PATH\fR): Automatically run \fBbundle clean\fR after installing when an explicit \fBpath\fR has not been set and Bundler is not installing into the system gems\. . .IP "\(bu" 4 \fBauto_install\fR (\fBBUNDLE_AUTO_INSTALL\fR): Automatically run \fBbundle install\fR when gems are missing\. . .IP "\(bu" 4 \fBbin\fR (\fBBUNDLE_BIN\fR): Install executables from gems in the bundle to the specified directory\. Defaults to \fBfalse\fR\. . .IP "\(bu" 4 \fBcache_all\fR (\fBBUNDLE_CACHE_ALL\fR): Cache all gems, including path and git gems\. This needs to be explicitly configured on bundler 1 and bundler 2, but will be the default on bundler 3\. . .IP "\(bu" 4 \fBcache_all_platforms\fR (\fBBUNDLE_CACHE_ALL_PLATFORMS\fR): Cache gems for all platforms\. . .IP "\(bu" 4 \fBcache_path\fR (\fBBUNDLE_CACHE_PATH\fR): The directory that bundler will place cached gems in when running \fBbundle package\fR, and that bundler will look in when installing gems\. Defaults to \fBvendor/cache\fR\. . .IP "\(bu" 4 \fBclean\fR (\fBBUNDLE_CLEAN\fR): Whether Bundler should run \fBbundle clean\fR automatically after \fBbundle install\fR\. . .IP "\(bu" 4 \fBconsole\fR (\fBBUNDLE_CONSOLE\fR): The console that \fBbundle console\fR starts\. Defaults to \fBirb\fR\. . .IP "\(bu" 4 \fBdefault_install_uses_path\fR (\fBBUNDLE_DEFAULT_INSTALL_USES_PATH\fR): Whether a \fBbundle install\fR without an explicit \fB\-\-path\fR argument defaults to installing gems in \fB\.bundle\fR\. . .IP "\(bu" 4 \fBdeployment\fR (\fBBUNDLE_DEPLOYMENT\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\. . .IP "\(bu" 4 \fBdisable_checksum_validation\fR (\fBBUNDLE_DISABLE_CHECKSUM_VALIDATION\fR): Allow installing gems even if they do not match the checksum provided by RubyGems\. . .IP "\(bu" 4 \fBdisable_exec_load\fR (\fBBUNDLE_DISABLE_EXEC_LOAD\fR): Stop Bundler from using \fBload\fR to launch an executable in\-process in \fBbundle exec\fR\. . .IP "\(bu" 4 \fBdisable_local_branch_check\fR (\fBBUNDLE_DISABLE_LOCAL_BRANCH_CHECK\fR): Allow Bundler to use a local git override without a branch specified in the Gemfile\. . .IP "\(bu" 4 \fBdisable_local_revision_check\fR (\fBBUNDLE_DISABLE_LOCAL_REVISION_CHECK\fR): Allow Bundler to use a local git override without checking if the revision present in the lockfile is present in the repository\. . .IP "\(bu" 4 \fBdisable_shared_gems\fR (\fBBUNDLE_DISABLE_SHARED_GEMS\fR): Stop Bundler from accessing gems installed to RubyGems\' normal location\. . .IP "\(bu" 4 \fBdisable_version_check\fR (\fBBUNDLE_DISABLE_VERSION_CHECK\fR): Stop Bundler from checking if a newer Bundler version is available on rubygems\.org\. . .IP "\(bu" 4 \fBforce_ruby_platform\fR (\fBBUNDLE_FORCE_RUBY_PLATFORM\fR): Ignore the current machine\'s platform and install only \fBruby\fR platform gems\. As a result, gems with native extensions will be compiled from source\. . .IP "\(bu" 4 \fBfrozen\fR (\fBBUNDLE_FROZEN\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\. Defaults to \fBtrue\fR when \fB\-\-deployment\fR is used\. . .IP "\(bu" 4 \fBgem\.github_username\fR (\fBBUNDLE_GEM__GITHUB_USERNAME\fR): Sets a GitHub username or organization to be used in \fBREADME\fR file when you create a new gem via \fBbundle gem\fR command\. It can be overridden by passing an explicit \fB\-\-github\-username\fR flag to \fBbundle gem\fR\. . .IP "\(bu" 4 \fBgem\.push_key\fR (\fBBUNDLE_GEM__PUSH_KEY\fR): Sets the \fB\-\-key\fR parameter for \fBgem push\fR when using the \fBrake release\fR command with a private gemstash server\. . .IP "\(bu" 4 \fBgemfile\fR (\fBBUNDLE_GEMFILE\fR): The name of the file that bundler should use as the \fBGemfile\fR\. This location of this file also sets the root of the project, which is used to resolve relative paths in the \fBGemfile\fR, among other things\. By default, bundler will search up from the current working directory until it finds a \fBGemfile\fR\. . .IP "\(bu" 4 \fBglobal_gem_cache\fR (\fBBUNDLE_GLOBAL_GEM_CACHE\fR): Whether Bundler should cache all gems globally, rather than locally to the installing Ruby installation\. . .IP "\(bu" 4 \fBignore_messages\fR (\fBBUNDLE_IGNORE_MESSAGES\fR): When set, no post install messages will be printed\. To silence a single gem, use dot notation like \fBignore_messages\.httparty true\fR\. . .IP "\(bu" 4 \fBinit_gems_rb\fR (\fBBUNDLE_INIT_GEMS_RB\fR): Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\. . .IP "\(bu" 4 \fBjobs\fR (\fBBUNDLE_JOBS\fR): The number of gems Bundler can install in parallel\. Defaults to 1 on Windows, and to the the number of processors on other platforms\. . .IP "\(bu" 4 \fBno_install\fR (\fBBUNDLE_NO_INSTALL\fR): Whether \fBbundle package\fR should skip installing gems\. . .IP "\(bu" 4 \fBno_prune\fR (\fBBUNDLE_NO_PRUNE\fR): Whether Bundler should leave outdated gems unpruned when caching\. . .IP "\(bu" 4 \fBpath\fR (\fBBUNDLE_PATH\fR): The location on disk where all gems in your bundle will be located regardless of \fB$GEM_HOME\fR or \fB$GEM_PATH\fR values\. Bundle gems not found in this location will be installed by \fBbundle install\fR\. Defaults to \fBGem\.dir\fR\. When \-\-deployment is used, defaults to vendor/bundle\. . .IP "\(bu" 4 \fBpath\.system\fR (\fBBUNDLE_PATH__SYSTEM\fR): Whether Bundler will install gems into the default system path (\fBGem\.dir\fR)\. . .IP "\(bu" 4 \fBpath_relative_to_cwd\fR (\fBBUNDLE_PATH_RELATIVE_TO_CWD\fR) Makes \fB\-\-path\fR relative to the CWD instead of the \fBGemfile\fR\. . .IP "\(bu" 4 \fBplugins\fR (\fBBUNDLE_PLUGINS\fR): Enable Bundler\'s experimental plugin system\. . .IP "\(bu" 4 \fBprefer_patch\fR (BUNDLE_PREFER_PATCH): Prefer updating only to next patch version during updates\. Makes \fBbundle update\fR calls equivalent to \fBbundler update \-\-patch\fR\. . .IP "\(bu" 4 \fBprint_only_version_number\fR (\fBBUNDLE_PRINT_ONLY_VERSION_NUMBER\fR): Print only version number from \fBbundler \-\-version\fR\. . .IP "\(bu" 4 \fBredirect\fR (\fBBUNDLE_REDIRECT\fR): The number of redirects allowed for network requests\. Defaults to \fB5\fR\. . .IP "\(bu" 4 \fBretry\fR (\fBBUNDLE_RETRY\fR): The number of times to retry failed network requests\. Defaults to \fB3\fR\. . .IP "\(bu" 4 \fBsetup_makes_kernel_gem_public\fR (\fBBUNDLE_SETUP_MAKES_KERNEL_GEM_PUBLIC\fR): Have \fBBundler\.setup\fR make the \fBKernel#gem\fR method public, even though RubyGems declares it as private\. . .IP "\(bu" 4 \fBshebang\fR (\fBBUNDLE_SHEBANG\fR): The program name that should be invoked for generated binstubs\. Defaults to the ruby install name used to generate the binstub\. . .IP "\(bu" 4 \fBsilence_deprecations\fR (\fBBUNDLE_SILENCE_DEPRECATIONS\fR): Whether Bundler should silence deprecation warnings for behavior that will be changed in the next major version\. . .IP "\(bu" 4 \fBsilence_root_warning\fR (\fBBUNDLE_SILENCE_ROOT_WARNING\fR): Silence the warning Bundler prints when installing gems as root\. . .IP "\(bu" 4 \fBssl_ca_cert\fR (\fBBUNDLE_SSL_CA_CERT\fR): Path to a designated CA certificate file or folder containing multiple certificates for trusted CAs in PEM format\. . .IP "\(bu" 4 \fBssl_client_cert\fR (\fBBUNDLE_SSL_CLIENT_CERT\fR): Path to a designated file containing a X\.509 client certificate and key in PEM format\. . .IP "\(bu" 4 \fBssl_verify_mode\fR (\fBBUNDLE_SSL_VERIFY_MODE\fR): The SSL verification mode Bundler uses when making HTTPS requests\. Defaults to verify peer\. . .IP "\(bu" 4 \fBsuppress_install_using_messages\fR (\fBBUNDLE_SUPPRESS_INSTALL_USING_MESSAGES\fR): Avoid printing \fBUsing \.\.\.\fR messages during installation when the version of a gem has not changed\. . .IP "\(bu" 4 \fBsystem_bindir\fR (\fBBUNDLE_SYSTEM_BINDIR\fR): The location where RubyGems installs binstubs\. Defaults to \fBGem\.bindir\fR\. . .IP "\(bu" 4 \fBtimeout\fR (\fBBUNDLE_TIMEOUT\fR): The seconds allowed before timing out for network requests\. Defaults to \fB10\fR\. . .IP "\(bu" 4 \fBupdate_requires_all_flag\fR (\fBBUNDLE_UPDATE_REQUIRES_ALL_FLAG\fR): Require passing \fB\-\-all\fR to \fBbundle update\fR when everything should be updated, and disallow passing no options to \fBbundle update\fR\. . .IP "\(bu" 4 \fBuser_agent\fR (\fBBUNDLE_USER_AGENT\fR): The custom user agent fragment Bundler includes in API requests\. . .IP "\(bu" 4 \fBwith\fR (\fBBUNDLE_WITH\fR): A \fB:\fR\-separated list of groups whose gems bundler should install\. . .IP "\(bu" 4 \fBwithout\fR (\fBBUNDLE_WITHOUT\fR): A \fB:\fR\-separated list of groups whose gems bundler should not install\. . .IP "" 0 . .P In general, you should set these settings per\-application by using the applicable flag to the bundle install(1) \fIbundle\-install\.1\.html\fR or bundle package(1) \fIbundle\-package\.1\.html\fR command\. . .P You can set them globally either via environment variables or \fBbundle config\fR, whichever is preferable for your setup\. If you use both, environment variables will take preference over global settings\. . .SH "LOCAL GIT REPOS" Bundler also allows you to work against a git repository locally instead of using the remote version\. This can be achieved by setting up a local override: . .IP "" 4 . .nf bundle config set \-\-local local\.GEM_NAME /path/to/local/git/repository . .fi . .IP "" 0 . .P For example, in order to use a local Rack repository, a developer could call: . .IP "" 4 . .nf bundle config set \-\-local local\.rack ~/Work/git/rack . .fi . .IP "" 0 . .P Now instead of checking out the remote git repository, the local override will be used\. Similar to a path source, every time the local git repository change, changes will be automatically picked up by Bundler\. This means a commit in the local git repo will update the revision in the \fBGemfile\.lock\fR to the local git repo revision\. This requires the same attention as git submodules\. Before pushing to the remote, you need to ensure the local override was pushed, otherwise you may point to a commit that only exists in your local machine\. You\'ll also need to CGI escape your usernames and passwords as well\. . .P Bundler does many checks to ensure a developer won\'t work with invalid references\. Particularly, we force a developer to specify a branch in the \fBGemfile\fR in order to use this feature\. If the branch specified in the \fBGemfile\fR and the current branch in the local git repository do not match, Bundler will abort\. This ensures that a developer is always working against the correct branches, and prevents accidental locking to a different branch\. . .P Finally, Bundler also ensures that the current revision in the \fBGemfile\.lock\fR exists in the local git repository\. By doing this, Bundler forces you to fetch the latest changes in the remotes\. . .SH "MIRRORS OF GEM SOURCES" Bundler supports overriding gem sources with mirrors\. This allows you to configure rubygems\.org as the gem source in your Gemfile while still using your mirror to fetch gems\. . .IP "" 4 . .nf bundle config set \-\-global mirror\.SOURCE_URL MIRROR_URL . .fi . .IP "" 0 . .P For example, to use a mirror of rubygems\.org hosted at rubygems\-mirror\.org: . .IP "" 4 . .nf bundle config set \-\-global mirror\.http://rubygems\.org http://rubygems\-mirror\.org . .fi . .IP "" 0 . .P Each mirror also provides a fallback timeout setting\. If the mirror does not respond within the fallback timeout, Bundler will try to use the original server instead of the mirror\. . .IP "" 4 . .nf bundle config set \-\-global mirror\.SOURCE_URL\.fallback_timeout TIMEOUT . .fi . .IP "" 0 . .P For example, to fall back to rubygems\.org after 3 seconds: . .IP "" 4 . .nf bundle config set \-\-global mirror\.https://rubygems\.org\.fallback_timeout 3 . .fi . .IP "" 0 . .P The default fallback timeout is 0\.1 seconds, but the setting can currently only accept whole seconds (for example, 1, 15, or 30)\. . .SH "CREDENTIALS FOR GEM SOURCES" Bundler allows you to configure credentials for any gem source, which allows you to avoid putting secrets into your Gemfile\. . .IP "" 4 . .nf bundle config set \-\-global SOURCE_HOSTNAME USERNAME:PASSWORD . .fi . .IP "" 0 . .P For example, to save the credentials of user \fBclaudette\fR for the gem source at \fBgems\.longerous\.com\fR, you would run: . .IP "" 4 . .nf bundle config set \-\-global gems\.longerous\.com claudette:s00pers3krit . .fi . .IP "" 0 . .P Or you can set the credentials as an environment variable like this: . .IP "" 4 . .nf export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit" . .fi . .IP "" 0 . .P For gems with a git source with HTTP(S) URL you can specify credentials like so: . .IP "" 4 . .nf bundle config set \-\-global https://github\.com/rubygems/rubygems\.git username:password . .fi . .IP "" 0 . .P Or you can set the credentials as an environment variable like so: . .IP "" 4 . .nf export BUNDLE_GITHUB__COM=username:password . .fi . .IP "" 0 . .P This is especially useful for private repositories on hosts such as Github, where you can use personal OAuth tokens: . .IP "" 4 . .nf export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x\-oauth\-basic . .fi . .IP "" 0 . .P Note that any configured credentials will be redacted by informative commands such as \fBbundle config list\fR or \fBbundle config get\fR, unless you use the \fB\-\-parseable\fR flag\. This is to avoid unintentionally leaking credentials when copy\-pasting bundler output\. . .P Also note that to guarantee a sane mapping between valid environment variable names and valid host names, bundler makes the following transformations: . .IP "\(bu" 4 Any \fB\-\fR characters in a host name are mapped to a triple dash (\fB___\fR) in the corresponding environment variable\. . .IP "\(bu" 4 Any \fB\.\fR characters in a host name are mapped to a double dash (\fB__\fR) in the corresponding environment variable\. . .IP "" 0 . .P This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you\'ll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\. . .SH "CONFIGURE BUNDLER DIRECTORIES" Bundler\'s home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler\'s home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values . .IP "" 4 . .nf BUNDLE_USER_HOME : $HOME/\.bundle BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin . .fi . .IP "" 0 man/man1/bundle-cache.1 0000644 00000006304 15102661023 0010546 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-CACHE" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application . .SH "SYNOPSIS" \fBbundle cache\fR . .SH "DESCRIPTION" Copy all of the \fB\.gem\fR files needed to run the application into the \fBvendor/cache\fR directory\. In the future, when running [bundle install(1)][bundle\-install], use the gems in the cache in preference to the ones on \fBrubygems\.org\fR\. . .SH "GIT AND PATH GEMS" The \fBbundle cache\fR command can also package \fB:git\fR and \fB:path\fR dependencies besides \.gem files\. This needs to be explicitly enabled via the \fB\-\-all\fR option\. Once used, the \fB\-\-all\fR option will be remembered\. . .SH "SUPPORT FOR MULTIPLE PLATFORMS" When using gems that have different packages for different platforms, Bundler supports caching of gems for other platforms where the Gemfile has been resolved (i\.e\. present in the lockfile) in \fBvendor/cache\fR\. This needs to be enabled via the \fB\-\-all\-platforms\fR option\. This setting will be remembered in your local bundler configuration\. . .SH "REMOTE FETCHING" By default, if you run \fBbundle install(1)\fR](bundle\-install\.1\.html) after running bundle cache(1) \fIbundle\-cache\.1\.html\fR, bundler will still connect to \fBrubygems\.org\fR to check whether a platform\-specific gem exists for any of the gems in \fBvendor/cache\fR\. . .P For instance, consider this Gemfile(5): . .IP "" 4 . .nf source "https://rubygems\.org" gem "nokogiri" . .fi . .IP "" 0 . .P If you run \fBbundle cache\fR under C Ruby, bundler will retrieve the version of \fBnokogiri\fR for the \fB"ruby"\fR platform\. If you deploy to JRuby and run \fBbundle install\fR, bundler is forced to check to see whether a \fB"java"\fR platformed \fBnokogiri\fR exists\. . .P Even though the \fBnokogiri\fR gem for the Ruby platform is \fItechnically\fR acceptable on JRuby, it has a C extension that does not run on JRuby\. As a result, bundler will, by default, still connect to \fBrubygems\.org\fR to check whether it has a version of one of your gems more specific to your platform\. . .P This problem is also not limited to the \fB"java"\fR platform\. A similar (common) problem can happen when developing on Windows and deploying to Linux, or even when developing on OSX and deploying to Linux\. . .P If you know for sure that the gems packaged in \fBvendor/cache\fR are appropriate for the platform you are on, you can run \fBbundle install \-\-local\fR to skip checking for more appropriate gems, and use the ones in \fBvendor/cache\fR\. . .P One way to be sure that you have the right platformed versions of all your gems is to run \fBbundle cache\fR on an identical machine and check in the gems\. For instance, you can run \fBbundle cache\fR on an identical staging box during your staging process, and check in the \fBvendor/cache\fR before deploying to production\. . .P By default, bundle cache(1) \fIbundle\-cache\.1\.html\fR fetches and also installs the gems to the default location\. To package the dependencies to \fBvendor/cache\fR without installing them to the local install location, you can run \fBbundle cache \-\-no\-install\fR\. man/man1/bundle-list.1 0000644 00000001705 15102661023 0010456 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-LIST" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-list\fR \- List all the gems in the bundle . .SH "SYNOPSIS" \fBbundle list\fR [\-\-name\-only] [\-\-paths] [\-\-without\-group=GROUP[ GROUP\.\.\.]] [\-\-only\-group=GROUP[ GROUP\.\.\.]] . .SH "DESCRIPTION" Prints a list of all the gems in the bundle including their version\. . .P Example: . .P bundle list \-\-name\-only . .P bundle list \-\-paths . .P bundle list \-\-without\-group test . .P bundle list \-\-only\-group dev . .P bundle list \-\-only\-group dev test \-\-paths . .SH "OPTIONS" . .TP \fB\-\-name\-only\fR Print only the name of each gem\. . .TP \fB\-\-paths\fR Print the path to each gem in the bundle\. . .TP \fB\-\-without\-group=<list>\fR A space\-separated list of groups of gems to skip during printing\. . .TP \fB\-\-only\-group=<list>\fR A space\-separated list of groups of gems to print\. man/man1/bundle-init.1 0000644 00000002067 15102661023 0010450 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-INIT" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-init\fR \- Generates a Gemfile into the current working directory . .SH "SYNOPSIS" \fBbundle init\fR [\-\-gemspec=FILE] . .SH "DESCRIPTION" Init generates a default [\fBGemfile(5)\fR][Gemfile(5)] in the current working directory\. When adding a [\fBGemfile(5)\fR][Gemfile(5)] to a gem with a gemspec, the \fB\-\-gemspec\fR option will automatically add each dependency listed in the gemspec file to the newly created [\fBGemfile(5)\fR][Gemfile(5)]\. . .SH "OPTIONS" . .TP \fB\-\-gemspec\fR Use the specified \.gemspec to create the [\fBGemfile(5)\fR][Gemfile(5)] . .SH "FILES" Included in the default [\fBGemfile(5)\fR][Gemfile(5)] generated is the line \fB# frozen_string_literal: true\fR\. This is a magic comment supported for the first time in Ruby 2\.3\. The presence of this line results in all string literals in the file being implicitly frozen\. . .SH "SEE ALSO" Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR man/man1/bundle-viz.1 0000644 00000002121 15102661023 0010304 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-VIZ" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile . .SH "SYNOPSIS" \fBbundle viz\fR [\-\-file=FILE] [\-\-format=FORMAT] [\-\-requirements] [\-\-version] [\-\-without=GROUP GROUP] . .SH "DESCRIPTION" \fBviz\fR generates a PNG file of the current \fBGemfile(5)\fR as a dependency graph\. \fBviz\fR requires the ruby\-graphviz gem (and its dependencies)\. . .P The associated gems must also be installed via \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR\. . .SH "OPTIONS" . .TP \fB\-\-file\fR, \fB\-f\fR The name to use for the generated file\. See \fB\-\-format\fR option . .TP \fB\-\-format\fR, \fB\-F\fR This is output format option\. Supported format is png, jpg, svg, dot \.\.\. . .TP \fB\-\-requirements\fR, \fB\-R\fR Set to show the version of each required dependency\. . .TP \fB\-\-version\fR, \fB\-v\fR Set to show each gem version\. . .TP \fB\-\-without\fR, \fB\-W\fR Exclude gems that are part of the specified named group\. man/man1/bundle-clean.1 0000644 00000001134 15102661023 0010561 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-CLEAN" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory . .SH "SYNOPSIS" \fBbundle clean\fR [\-\-dry\-run] [\-\-force] . .SH "DESCRIPTION" This command will remove all unused gems in your bundler directory\. This is useful when you have made many changes to your gem dependencies\. . .SH "OPTIONS" . .TP \fB\-\-dry\-run\fR Print the changes, but do not clean the unused gems\. . .TP \fB\-\-force\fR Force a clean even if \fB\-\-path\fR is not set\. man/man1/ri.1 0000644 00000012343 15102661023 0006646 0 ustar 00 .\"Ruby is copyrighted by Yukihiro Matsumoto <matz@netlab.jp>. .Dd April 20, 2017 .Dt RI \&1 "Ruby Programmer's Reference Guide" .Os UNIX .Sh NAME .Nm ri .Nd Ruby API reference front end .Sh SYNOPSIS .Nm .Op Fl ahilTv .Op Fl d Ar DIRNAME .Op Fl f Ar FORMAT .Op Fl w Ar WIDTH .Op Fl - Ns Oo Cm no- Oc Ns Cm pager .Op Fl -server Ns Oo = Ns Ar PORT Oc .Op Fl - Ns Oo Cm no- Oc Ns Cm list-doc-dirs .Op Fl -no-standard-docs .Op Fl - Ns Oo Cm no- Oc Ns Bro Cm system Ns | Ns Cm site Ns | Ns Cm gems Ns | Ns Cm home Brc .Op Fl - Ns Oo Cm no- Oc Ns Cm profile .Op Fl -dump Ns = Ns Ar CACHE .Op Ar name ... .Sh DESCRIPTION .Nm is a command-line front end for the Ruby API reference. You can search and read the API reference for classes and methods with .Nm . .Pp .Nm is a part of Ruby. .Pp .Ar name can be: .Bl -diag -offset indent .It Class | Module | Module::Class .Pp .It Class::method | Class#method | Class.method | method .Pp .It gem_name: | gem_name:README | gem_name:History .El .Pp All class names may be abbreviated to their minimum unambiguous form. If a name is ambiguous, all valid options will be listed. .Pp A .Ql \&. matches either class or instance methods, while #method matches only instance and ::method matches only class methods. .Pp README and other files may be displayed by prefixing them with the gem name they're contained in. If the gem name is followed by a .Ql \&: all files in the gem will be shown. The file name extension may be omitted where it is unambiguous. .Pp For example: .Bd -literal -offset indent ri Fil ri File ri File.new ri zip ri rdoc:README .Ed .Pp Note that shell quoting or escaping may be required for method names containing punctuation: .Bd -literal -offset indent ri 'Array.[]' ri compact\e! .Ed .Pp To see the default directories .Nm will search, run: .Bd -literal -offset indent ri --list-doc-dirs .Ed .Pp Specifying the .Fl -system , Fl -site , Fl -home , Fl -gems , or .Fl -doc-dir options will limit .Nm to searching only the specified directories. .Pp .Nm options may be set in the .Ev RI environment variable. .Pp The .Nm pager can be set with the .Ev RI_PAGER environment variable or the .Ev PAGER environment variable. .Pp .Sh OPTIONS .Bl -tag -width "1234567890123" -compact .Pp .It Fl i .It Fl - Ns Oo Cm no- Oc Ns Cm interactive In interactive mode you can repeatedly look up methods with autocomplete. .Pp .It Fl a .It Fl - Ns Oo Cm no- Oc Ns Cm all Show all documentation for a class or module. .Pp .It Fl l .It Fl - Ns Oo Cm no- Oc Ns Cm list List classes .Nm knows about. .Pp .It Fl - Ns Oo Cm no- Oc Ns Cm pager Send output to a pager, rather than directly to stdout. .Pp .It Fl T Synonym for .Fl -no-pager . .Pp .It Fl w Ar WIDTH .It Fl -width Ns = Ns Ar WIDTH Set the width of the output. .Pp .It Fl -server Ns Oo = Ns Ar PORT Oc Run RDoc server on the given port. The default port is\~8214. .Pp .It Fl f Ar FORMAT .It Fl -format Ns = Ns Ar FORMAT Use the selected formatter. The default formatter is .Li bs for paged output and .Li ansi otherwise. Valid formatters are: .Li ansi , Li bs , Li markdown , Li rdoc . .Pp .It Fl h .It Fl -help Show help and exit. .Pp .It Fl v .It Fl -version Output version information and exit. .El .Pp Data source options: .Bl -tag -width "1234567890123" -compact .Pp .It Fl - Ns Oo Cm no- Oc Ns Cm list-doc-dirs List the directories from which .Nm will source documentation on stdout and exit. .Pp .It Fl d Ar DIRNAME .It Fl -doc-dir Ns = Ns Ar DIRNAME List of directories from which to source documentation in addition to the standard directories. May be repeated. .Pp .It Fl -no-standard-docs Do not include documentation from the Ruby standard library, .Pa site_lib , installed gems, or .Pa ~/.rdoc . Use with .Fl -doc-dir . .Pp .It Fl - Ns Oo Cm no- Oc Ns Cm system Include documentation from Ruby's standard library. Defaults to true. .Pp .It Fl - Ns Oo Cm no- Oc Ns Cm site Include documentation from libraries installed in .Pa site_lib . Defaults to true. .Pp .It Fl - Ns Oo Cm no- Oc Ns Cm gems Include documentation from RubyGems. Defaults to true. .Pp .It Fl - Ns Oo Cm no- Oc Ns Cm home Include documentation stored in .Pa ~/.rdoc . Defaults to true. .El .Pp Debug options: .Bl -tag -width "1234567890123" -compact .Pp .It Fl - Ns Oo Cm no- Oc Ns Cm profile Run with the Ruby profiler. .Pp .It Fl -dump Ns = Ns Ar CACHE Dump data from an ri cache or data file. .El .Pp .Sh ENVIRONMENT .Bl -tag -width "USERPROFILE" -compact .Pp .It Ev RI Options to prepend to those specified on the command-line. .Pp .It Ev RI_PAGER .It Ev PAGER Pager program to use for displaying. .Pp .It Ev HOME .It Ev USERPROFILE .It Ev HOMEPATH Path to the user's home directory. .El .Pp .Sh FILES .Bl -tag -width "USERPROFILE" -compact .Pp .It Pa ~/.rdoc Path for ri data in the user's home directory. .Pp .El .Pp .Sh SEE ALSO .Xr ruby 1 , .Xr rdoc 1 , .Xr gem 1 .Pp .Sh REPORTING BUGS .Bl -bullet .It Security vulnerabilities should be reported via an email to .Mt security@ruby-lang.org . Reported problems will be published after being fixed. .Pp .It Other bugs and feature requests can be reported via the Ruby Issue Tracking System .Pq Lk https://bugs.ruby-lang.org/ . Do not report security vulnerabilities via this system because it publishes the vulnerabilities immediately. .El .Sh AUTHORS Written by .An Dave Thomas Aq dave@pragmaticprogrammer.com . man/man1/bundle-install.1 0000644 00000043110 15102661023 0011145 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-INSTALL" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile . .SH "SYNOPSIS" \fBbundle install\fR [\-\-binstubs[=DIRECTORY]] [\-\-clean] [\-\-deployment] [\-\-frozen] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-local] [\-\-no\-cache] [\-\-no\-prune] [\-\-path PATH] [\-\-quiet] [\-\-redownload] [\-\-retry=NUMBER] [\-\-shebang] [\-\-standalone[=GROUP[ GROUP\.\.\.]]] [\-\-system] [\-\-trust\-policy=POLICY] [\-\-with=GROUP[ GROUP\.\.\.]] [\-\-without=GROUP[ GROUP\.\.\.]] . .SH "DESCRIPTION" Install the gems specified in your Gemfile(5)\. If this is the first time you run bundle install (and a \fBGemfile\.lock\fR does not exist), Bundler will fetch all remote sources, resolve dependencies and install all needed gems\. . .P If a \fBGemfile\.lock\fR does exist, and you have not updated your Gemfile(5), Bundler will fetch all remote sources, but use the dependencies specified in the \fBGemfile\.lock\fR instead of resolving dependencies\. . .P If a \fBGemfile\.lock\fR does exist, and you have updated your Gemfile(5), Bundler will use the dependencies in the \fBGemfile\.lock\fR for all gems that you did not update, but will re\-resolve the dependencies of gems that you did update\. You can find more information about this update process below under \fICONSERVATIVE UPDATING\fR\. . .SH "OPTIONS" The \fB\-\-clean\fR, \fB\-\-deployment\fR, \fB\-\-frozen\fR, \fB\-\-no\-prune\fR, \fB\-\-path\fR, \fB\-\-shebang\fR, \fB\-\-system\fR, \fB\-\-without\fR and \fB\-\-with\fR options are deprecated because they only make sense if they are applied to every subsequent \fBbundle install\fR run automatically and that requires \fBbundler\fR to silently remember them\. Since \fBbundler\fR will no longer remember CLI flags in future versions, \fBbundle config\fR (see bundle\-config(1)) should be used to apply them permanently\. . .TP \fB\-\-binstubs[=<directory>]\fR Binstubs are scripts that wrap around executables\. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it in \fBbin/\fR\. This lets you link the binstub inside of an application to the exact gem version the application needs\. . .IP Creates a directory (defaults to \fB~/bin\fR) and places any executables from the gem there\. These executables run in Bundler\'s context\. If used, you might add this directory to your environment\'s \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\. . .TP \fB\-\-clean\fR On finishing the installation Bundler is going to remove any gems not present in the current Gemfile(5)\. Don\'t worry, gems currently in use will not be removed\. . .IP This option is deprecated in favor of the \fBclean\fR setting\. . .TP \fB\-\-deployment\fR In \fIdeployment mode\fR, Bundler will \'roll\-out\' the bundle for production or CI use\. Please check carefully if you want to have this option enabled in your development environment\. . .IP This option is deprecated in favor of the \fBdeployment\fR setting\. . .TP \fB\-\-redownload\fR Force download every gem, even if the required versions are already available locally\. . .TP \fB\-\-frozen\fR Do not allow the Gemfile\.lock to be updated after this install\. Exits non\-zero if there are going to be changes to the Gemfile\.lock\. . .IP This option is deprecated in favor of the \fBfrozen\fR setting\. . .TP \fB\-\-full\-index\fR Bundler will not call Rubygems\' API endpoint (default) but download and cache a (currently big) index file of all gems\. Performance can be improved for large bundles that seldom change by enabling this option\. . .TP \fB\-\-gemfile=<gemfile>\fR The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project\'s root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\. . .TP \fB\-\-jobs=[<number>]\fR, \fB\-j[<number>]\fR The maximum number of parallel download and install jobs\. The default is \fB1\fR\. . .TP \fB\-\-local\fR Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems\' cache or in \fBvendor/cache\fR\. Note that if an appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\. . .TP \fB\-\-no\-cache\fR Do not update the cache in \fBvendor/cache\fR with the newly bundled gems\. This does not remove any gems in the cache but keeps the newly bundled gems from being cached during the install\. . .TP \fB\-\-no\-prune\fR Don\'t remove stale gems from the cache when the installation finishes\. . .IP This option is deprecated in favor of the \fBno_prune\fR setting\. . .TP \fB\-\-path=<path>\fR The location to install the specified gems to\. This defaults to Rubygems\' setting\. Bundler shares this location with Rubygems, \fBgem install \.\.\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \.\.\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\. . .IP This option is deprecated in favor of the \fBpath\fR setting\. . .TP \fB\-\-quiet\fR Do not print progress information to the standard output\. Instead, Bundler will exit using a status code (\fB$?\fR)\. . .TP \fB\-\-retry=[<number>]\fR Retry failed network or git requests for \fInumber\fR times\. . .TP \fB\-\-shebang=<ruby\-executable>\fR Uses the specified ruby executable (usually \fBruby\fR) to execute the scripts created with \fB\-\-binstubs\fR\. In addition, if you use \fB\-\-binstubs\fR together with \fB\-\-shebang jruby\fR these executables will be changed to execute \fBjruby\fR instead\. . .IP This option is deprecated in favor of the \fBshebang\fR setting\. . .TP \fB\-\-standalone[=<list>]\fR Makes a bundle that can work without depending on Rubygems or Bundler at runtime\. A space separated list of groups to install has to be specified\. Bundler creates a directory named \fBbundle\fR and installs the bundle there\. It also generates a \fBbundle/bundler/setup\.rb\fR file to replace Bundler\'s own setup in the manner required\. Using this option implicitly sets \fBpath\fR, which is a [remembered option][REMEMBERED OPTIONS]\. . .TP \fB\-\-system\fR Installs the gems specified in the bundle to the system\'s Rubygems location\. This overrides any previous configuration of \fB\-\-path\fR\. . .IP This option is deprecated in favor of the \fBsystem\fR setting\. . .TP \fB\-\-trust\-policy=[<policy>]\fR Apply the Rubygems security policy \fIpolicy\fR, where policy is one of \fBHighSecurity\fR, \fBMediumSecurity\fR, \fBLowSecurity\fR, \fBAlmostNoSecurity\fR, or \fBNoSecurity\fR\. For more details, please see the Rubygems signing documentation linked below in \fISEE ALSO\fR\. . .TP \fB\-\-with=<list>\fR A space\-separated list of groups referencing gems to install\. If an optional group is given it is installed\. If a group is given that is in the remembered list of groups given to \-\-without, it is removed from that list\. . .IP This option is deprecated in favor of the \fBwith\fR setting\. . .TP \fB\-\-without=<list>\fR A space\-separated list of groups referencing gems to skip during installation\. If a group is given that is in the remembered list of groups given to \-\-with, it is removed from that list\. . .IP This option is deprecated in favor of the \fBwithout\fR setting\. . .SH "DEPLOYMENT MODE" Bundler\'s defaults are optimized for development\. To switch to defaults optimized for deployment and for CI, use the \fB\-\-deployment\fR flag\. Do not activate deployment mode on development machines, as it will cause an error when the Gemfile(5) is modified\. . .IP "1." 4 A \fBGemfile\.lock\fR is required\. . .IP To ensure that the same versions of the gems you developed with and tested with are also used in deployments, a \fBGemfile\.lock\fR is required\. . .IP This is mainly to ensure that you remember to check your \fBGemfile\.lock\fR into version control\. . .IP "2." 4 The \fBGemfile\.lock\fR must be up to date . .IP In development, you can modify your Gemfile(5) and re\-run \fBbundle install\fR to \fIconservatively update\fR your \fBGemfile\.lock\fR snapshot\. . .IP In deployment, your \fBGemfile\.lock\fR should be up\-to\-date with changes made in your Gemfile(5)\. . .IP "3." 4 Gems are installed to \fBvendor/bundle\fR not your default system location . .IP In development, it\'s convenient to share the gems used in your application with other applications and other scripts that run on the system\. . .IP In deployment, isolation is a more important default\. In addition, the user deploying the application may not have permission to install gems to the system, or the web server may not have permission to read them\. . .IP As a result, \fBbundle install \-\-deployment\fR installs gems to the \fBvendor/bundle\fR directory in the application\. This may be overridden using the \fB\-\-path\fR option\. . .IP "" 0 . .SH "SUDO USAGE" By default, Bundler installs gems to the same location as \fBgem install\fR\. . .P In some cases, that location may not be writable by your Unix user\. In that case, Bundler will stage everything in a temporary directory, then ask you for your \fBsudo\fR password in order to copy the gems into their system location\. . .P From your perspective, this is identical to installing the gems directly into the system\. . .P You should never use \fBsudo bundle install\fR\. This is because several other steps in \fBbundle install\fR must be performed as the current user: . .IP "\(bu" 4 Updating your \fBGemfile\.lock\fR . .IP "\(bu" 4 Updating your \fBvendor/cache\fR, if necessary . .IP "\(bu" 4 Checking out private git repositories using your user\'s SSH keys . .IP "" 0 . .P Of these three, the first two could theoretically be performed by \fBchown\fRing the resulting files to \fB$SUDO_USER\fR\. The third, however, can only be performed by invoking the \fBgit\fR command as the current user\. Therefore, git gems are downloaded and installed into \fB~/\.bundle\fR rather than $GEM_HOME or $BUNDLE_PATH\. . .P As a result, you should run \fBbundle install\fR as the current user, and Bundler will ask for your password if it is needed to put the gems into their final location\. . .SH "INSTALLING GROUPS" By default, \fBbundle install\fR will install all gems in all groups in your Gemfile(5), except those declared for a different platform\. . .P However, you can explicitly tell Bundler to skip installing certain groups with the \fB\-\-without\fR option\. This option takes a space\-separated list of groups\. . .P While the \fB\-\-without\fR option will skip \fIinstalling\fR the gems in the specified groups, it will still \fIdownload\fR those gems and use them to resolve the dependencies of every gem in your Gemfile(5)\. . .P This is so that installing a different set of groups on another machine (such as a production server) will not change the gems and versions that you have already developed and tested against\. . .P \fBBundler offers a rock\-solid guarantee that the third\-party code you are running in development and testing is also the third\-party code you are running in production\. You can choose to exclude some of that code in different environments, but you will never be caught flat\-footed by different versions of third\-party code being used in different environments\.\fR . .P For a simple illustration, consider the following Gemfile(5): . .IP "" 4 . .nf source \'https://rubygems\.org\' gem \'sinatra\' group :production do gem \'rack\-perftools\-profiler\' end . .fi . .IP "" 0 . .P In this case, \fBsinatra\fR depends on any version of Rack (\fB>= 1\.0\fR), while \fBrack\-perftools\-profiler\fR depends on 1\.x (\fB~> 1\.0\fR)\. . .P When you run \fBbundle install \-\-without production\fR in development, we look at the dependencies of \fBrack\-perftools\-profiler\fR as well\. That way, you do not spend all your time developing against Rack 2\.0, using new APIs unavailable in Rack 1\.x, only to have Bundler switch to Rack 1\.2 when the \fBproduction\fR group \fIis\fR used\. . .P This should not cause any problems in practice, because we do not attempt to \fBinstall\fR the gems in the excluded groups, and only evaluate as part of the dependency resolution process\. . .P This also means that you cannot include different versions of the same gem in different groups, because doing so would result in different sets of dependencies used in development and production\. Because of the vagaries of the dependency resolution process, this usually affects more than the gems you list in your Gemfile(5), and can (surprisingly) radically change the gems you are using\. . .SH "THE GEMFILE\.LOCK" When you run \fBbundle install\fR, Bundler will persist the full names and versions of all gems that you used (including dependencies of the gems specified in the Gemfile(5)) into a file called \fBGemfile\.lock\fR\. . .P Bundler uses this file in all subsequent calls to \fBbundle install\fR, which guarantees that you always use the same exact code, even as your application moves across machines\. . .P Because of the way dependency resolution works, even a seemingly small change (for instance, an update to a point\-release of a dependency of a gem in your Gemfile(5)) can result in radically different gems being needed to satisfy all dependencies\. . .P As a result, you \fBSHOULD\fR check your \fBGemfile\.lock\fR into version control, in both applications and gems\. If you do not, every machine that checks out your repository (including your production server) will resolve all dependencies again, which will result in different versions of third\-party code being used if \fBany\fR of the gems in the Gemfile(5) or any of their dependencies have been updated\. . .P When Bundler first shipped, the \fBGemfile\.lock\fR was included in the \fB\.gitignore\fR file included with generated gems\. Over time, however, it became clear that this practice forces the pain of broken dependencies onto new contributors, while leaving existing contributors potentially unaware of the problem\. Since \fBbundle install\fR is usually the first step towards a contribution, the pain of broken dependencies would discourage new contributors from contributing\. As a result, we have revised our guidance for gem authors to now recommend checking in the lock for gems\. . .SH "CONSERVATIVE UPDATING" When you make a change to the Gemfile(5) and then run \fBbundle install\fR, Bundler will update only the gems that you modified\. . .P In other words, if a gem that you \fBdid not modify\fR worked before you called \fBbundle install\fR, it will continue to use the exact same versions of all dependencies as it used before the update\. . .P Let\'s take a look at an example\. Here\'s your original Gemfile(5): . .IP "" 4 . .nf source \'https://rubygems\.org\' gem \'actionpack\', \'2\.3\.8\' gem \'activemerchant\' . .fi . .IP "" 0 . .P In this case, both \fBactionpack\fR and \fBactivemerchant\fR depend on \fBactivesupport\fR\. The \fBactionpack\fR gem depends on \fBactivesupport 2\.3\.8\fR and \fBrack ~> 1\.1\.0\fR, while the \fBactivemerchant\fR gem depends on \fBactivesupport >= 2\.3\.2\fR, \fBbraintree >= 2\.0\.0\fR, and \fBbuilder >= 2\.0\.0\fR\. . .P When the dependencies are first resolved, Bundler will select \fBactivesupport 2\.3\.8\fR, which satisfies the requirements of both gems in your Gemfile(5)\. . .P Next, you modify your Gemfile(5) to: . .IP "" 4 . .nf source \'https://rubygems\.org\' gem \'actionpack\', \'3\.0\.0\.rc\' gem \'activemerchant\' . .fi . .IP "" 0 . .P The \fBactionpack 3\.0\.0\.rc\fR gem has a number of new dependencies, and updates the \fBactivesupport\fR dependency to \fB= 3\.0\.0\.rc\fR and the \fBrack\fR dependency to \fB~> 1\.2\.1\fR\. . .P When you run \fBbundle install\fR, Bundler notices that you changed the \fBactionpack\fR gem, but not the \fBactivemerchant\fR gem\. It evaluates the gems currently being used to satisfy its requirements: . .TP \fBactivesupport 2\.3\.8\fR also used to satisfy a dependency in \fBactivemerchant\fR, which is not being updated . .TP \fBrack ~> 1\.1\.0\fR not currently being used to satisfy another dependency . .P Because you did not explicitly ask to update \fBactivemerchant\fR, you would not expect it to suddenly stop working after updating \fBactionpack\fR\. However, satisfying the new \fBactivesupport 3\.0\.0\.rc\fR dependency of actionpack requires updating one of its dependencies\. . .P Even though \fBactivemerchant\fR declares a very loose dependency that theoretically matches \fBactivesupport 3\.0\.0\.rc\fR, Bundler treats gems in your Gemfile(5) that have not changed as an atomic unit together with their dependencies\. In this case, the \fBactivemerchant\fR dependency is treated as \fBactivemerchant 1\.7\.1 + activesupport 2\.3\.8\fR, so \fBbundle install\fR will report that it cannot update \fBactionpack\fR\. . .P To explicitly update \fBactionpack\fR, including its dependencies which other gems in the Gemfile(5) still depend on, run \fBbundle update actionpack\fR (see \fBbundle update(1)\fR)\. . .P \fBSummary\fR: In general, after making a change to the Gemfile(5) , you should first try to run \fBbundle install\fR, which will guarantee that no other gem in the Gemfile(5) is impacted by the change\. If that does not work, run bundle update(1) \fIbundle\-update\.1\.html\fR\. . .SH "SEE ALSO" . .IP "\(bu" 4 Gem install docs \fIhttp://guides\.rubygems\.org/rubygems\-basics/#installing\-gems\fR . .IP "\(bu" 4 Rubygems signing docs \fIhttp://guides\.rubygems\.org/security/\fR . .IP "" 0 man/man1/bundle-outdated.1 0000644 00000007346 15102661023 0011323 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-OUTDATED" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-outdated\fR \- List installed gems with newer versions available . .SH "SYNOPSIS" \fBbundle outdated\fR [GEM] [\-\-local] [\-\-pre] [\-\-source] [\-\-strict] [\-\-parseable | \-\-porcelain] [\-\-group=GROUP] [\-\-groups] [\-\-update\-strict] [\-\-patch|\-\-minor|\-\-major] [\-\-filter\-major] [\-\-filter\-minor] [\-\-filter\-patch] [\-\-only\-explicit] . .SH "DESCRIPTION" Outdated lists the names and versions of gems that have a newer version available in the given source\. Calling outdated with [GEM [GEM]] will only check for newer versions of the given gems\. Prerelease gems are ignored by default\. If your gems are up to date, Bundler will exit with a status of 0\. Otherwise, it will exit 1\. . .SH "OPTIONS" . .TP \fB\-\-local\fR Do not attempt to fetch gems remotely and use the gem cache instead\. . .TP \fB\-\-pre\fR Check for newer pre\-release gems\. . .TP \fB\-\-source\fR Check against a specific source\. . .TP \fB\-\-strict\fR Only list newer versions allowed by your Gemfile requirements\. . .TP \fB\-\-parseable\fR, \fB\-\-porcelain\fR Use minimal formatting for more parseable output\. . .TP \fB\-\-group\fR List gems from a specific group\. . .TP \fB\-\-groups\fR List gems organized by groups\. . .TP \fB\-\-update\-strict\fR Strict conservative resolution, do not allow any gem to be updated past latest \-\-patch | \-\-minor| \-\-major\. . .TP \fB\-\-minor\fR Prefer updating only to next minor version\. . .TP \fB\-\-major\fR Prefer updating to next major version (default)\. . .TP \fB\-\-patch\fR Prefer updating only to next patch version\. . .TP \fB\-\-filter\-major\fR Only list major newer versions\. . .TP \fB\-\-filter\-minor\fR Only list minor newer versions\. . .TP \fB\-\-filter\-patch\fR Only list patch newer versions\. . .TP \fB\-\-only\-explicit\fR Only list gems specified in your Gemfile, not their dependencies\. . .SH "PATCH LEVEL OPTIONS" See bundle update(1) \fIbundle\-update\.1\.html\fR for details\. . .P One difference between the patch level options in \fBbundle update\fR and here is the \fB\-\-strict\fR option\. \fB\-\-strict\fR was already an option on outdated before the patch level options were added\. \fB\-\-strict\fR wasn\'t altered, and the \fB\-\-update\-strict\fR option on \fBoutdated\fR reflects what \fB\-\-strict\fR does on \fBbundle update\fR\. . .SH "FILTERING OUTPUT" The 3 filtering options do not affect the resolution of versions, merely what versions are shown in the output\. . .P If the regular output shows the following: . .IP "" 4 . .nf * faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test" * hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default" * headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test" . .fi . .IP "" 0 . .P \fB\-\-filter\-major\fR would only show: . .IP "" 4 . .nf * hashie (newest 3\.4\.6, installed 1\.2\.0, requested = 1\.2\.0) in groups "default" . .fi . .IP "" 0 . .P \fB\-\-filter\-minor\fR would only show: . .IP "" 4 . .nf * headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test" . .fi . .IP "" 0 . .P \fB\-\-filter\-patch\fR would only show: . .IP "" 4 . .nf * faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test" . .fi . .IP "" 0 . .P Filter options can be combined\. \fB\-\-filter\-minor\fR and \fB\-\-filter\-patch\fR would show: . .IP "" 4 . .nf * faker (newest 1\.6\.6, installed 1\.6\.5, requested ~> 1\.4) in groups "development, test" * headless (newest 2\.3\.1, installed 2\.2\.3) in groups "test" . .fi . .IP "" 0 . .P Combining all three \fBfilter\fR options would be the same result as providing none of them\. man/man1/bundle-lock.1 0000644 00000006167 15102661023 0010442 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-LOCK" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-lock\fR \- Creates / Updates a lockfile without installing . .SH "SYNOPSIS" \fBbundle lock\fR [\-\-update] [\-\-local] [\-\-print] [\-\-lockfile=PATH] [\-\-full\-index] [\-\-add\-platform] [\-\-remove\-platform] [\-\-patch] [\-\-minor] [\-\-major] [\-\-strict] [\-\-conservative] . .SH "DESCRIPTION" Lock the gems specified in Gemfile\. . .SH "OPTIONS" . .TP \fB\-\-update=<*gems>\fR Ignores the existing lockfile\. Resolve then updates lockfile\. Taking a list of gems or updating all gems if no list is given\. . .TP \fB\-\-local\fR Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems\' cache or in \fBvendor/cache\fR\. Note that if a appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\. . .TP \fB\-\-print\fR Prints the lockfile to STDOUT instead of writing to the file system\. . .TP \fB\-\-lockfile=<path>\fR The path where the lockfile should be written to\. . .TP \fB\-\-full\-index\fR Fall back to using the single\-file index of all gems\. . .TP \fB\-\-add\-platform\fR Add a new platform to the lockfile, re\-resolving for the addition of that platform\. . .TP \fB\-\-remove\-platform\fR Remove a platform from the lockfile\. . .TP \fB\-\-patch\fR If updating, prefer updating only to next patch version\. . .TP \fB\-\-minor\fR If updating, prefer updating only to next minor version\. . .TP \fB\-\-major\fR If updating, prefer updating to next major version (default)\. . .TP \fB\-\-strict\fR If updating, do not allow any gem to be updated past latest \-\-patch | \-\-minor | \-\-major\. . .TP \fB\-\-conservative\fR If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated\. . .SH "UPDATING ALL GEMS" If you run \fBbundle lock\fR with \fB\-\-update\fR option without list of gems, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\. . .SH "UPDATING A LIST OF GEMS" Sometimes, you want to update a single gem in the Gemfile(5), and leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\. . .P For instance, you only want to update \fBnokogiri\fR, run \fBbundle lock \-\-update nokogiri\fR\. . .P Bundler will update \fBnokogiri\fR and any of its dependencies, but leave the rest of the gems that you specified locked to the versions in the \fBGemfile\.lock\fR\. . .SH "SUPPORTING OTHER PLATFORMS" If you want your bundle to support platforms other than the one you\'re running locally, you can run \fBbundle lock \-\-add\-platform PLATFORM\fR to add PLATFORM to the lockfile, force bundler to re\-resolve and consider the new platform when picking gems, all without needing to have a machine that matches PLATFORM handy to install those platform\-specific gems on\. . .P For a full explanation of gem platforms, see \fBgem help platform\fR\. . .SH "PATCH LEVEL OPTIONS" See bundle update(1) \fIbundle\-update\.1\.html\fR for details\. man/man1/bundle-add.1 0000644 00000002722 15102661023 0010233 0 ustar 00 .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "BUNDLE\-ADD" "1" "December 2021" "" "" . .SH "NAME" \fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install . .SH "SYNOPSIS" \fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-git=GIT] [\-\-branch=BRANCH] [\-\-skip\-install] [\-\-strict] [\-\-optimistic] . .SH "DESCRIPTION" Adds the named gem to the Gemfile and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\. . .P Example: . .P bundle add rails . .P bundle add rails \-\-version "< 3\.0, > 1\.1" . .P bundle add rails \-\-version "~> 5\.0\.0" \-\-source "https://gems\.example\.com" \-\-group "development" . .P bundle add rails \-\-skip\-install . .P bundle add rails \-\-group "development, test" . .SH "OPTIONS" . .TP \fB\-\-version\fR, \fB\-v\fR Specify version requirements(s) for the added gem\. . .TP \fB\-\-group\fR, \fB\-g\fR Specify the group(s) for the added gem\. Multiple groups should be separated by commas\. . .TP \fB\-\-source\fR, , \fB\-s\fR Specify the source for the added gem\. . .TP \fB\-\-git\fR Specify the git source for the added gem\. . .TP \fB\-\-branch\fR Specify the git branch for the added gem\. . .TP \fB\-\-skip\-install\fR Adds the gem to the Gemfile but does not install it\. . .TP \fB\-\-optimistic\fR Adds optimistic declaration of version . .TP \fB\-\-strict\fR Adds strict declaration of version gems/cache/rackup-2.1.0.gem 0000644 00000037000 15102661023 0011151 0 ustar 00 metadata.gz 0000444 0000000 0000000 00000001271 14364637605 013455 0 ustar 00wheel wheel 0000000 0000000 � �?�c�W]o�0}��0{�S>� �,1 �@�eC �Pt��&f�cl��B�۹��ڮe�+K+%���>>>��(���拤�?a��B�Y,�Ldc���;(�[���<�bG��C�2iO�Ya�8M�Pm�����'���=!�С^��s0��l~n�C�riJ��;+Ѕ��AR>�1��ӣ(�D�G<ME�����߳-�M!��|{ϖ�j��,4��k觫bJ�H��:$��ɓ�>�����K� K|�kM� ���R��(�+� {!|C}�`��_�����O����(�P�5�B�w'�k"�8G�ر�����e��Ȁ>����*�G�S�RM*�Z�B�d�{�ߍ�#o�4�^�+��s����K�.����m�\ ;�qo�MW��J d�l�l&����mC��^ĕ� ��_gc����*�ګ���+<)*�Ӳq�UN�Fv���R�C�[�(�/H&�u�I�P��ٓ�N�EՍF9��z�$�u��E�QF���zM_�|�4��<�����4>�R�F�Y?%M?�ä���,��o��/�2��]snٴ�Un����[;���g�D���=���|;Ŷ�� �2�T�9.���U�1����?�t���'N��ɑ��{��&���jl�Ǭ[3�^��t��~ data.tar.gz 0000444 0000000 0000000 00000026114 14364637605 013376 0 ustar 00wheel wheel 0000000 0000000 � �?�c�}�v7�`�.��2!�Pԇ�/�6%�mIԐ�G��M;jv���9�߾��9�o�>��}��$[U ������q�L�4K�( ��P�*T�lwͷ���⛯�������o��O���ƃ�o6��?�zx���u�۸�7l��?�' B�T.g�;��T��?��ʽ�(��F��{��h�,����}��0}�=:v�}�i�Џx����l�}�X�}�Y��Xs�\G�R�~7�}�_p��K���#��y[�Y�ā��G_��nm����o=z�y��_��+��n|�"w�}�8;�=�n� ��-��}6�ls}ss��_g�%�[�;��8�5�ҥ廬����������V��K��c���P�DB��ҧ�GH��+��7�N�K�Mu�͚�Ts%�NJw���a��F��O�Ç[w�����ܒ���/Vp��e/:�Ww�o���*����R����Gw����,��w�B�݅ P؞[����K>���y�f��])���g��������������9Z�[a�KM<���=���w2������7�j���v�N4�S �����l�#v_u�Z1TD��,��sf���I� l��(������b�ȭΗ�bQ{�� k~��y�D�尙̠��8[X�+��r4�����=�6���>�|[�f%m�e����La��>_���ș�,1���z�Pl�@� ���)��L��"h��#H�@���aC�y�O+@2&��]I���7,��bb���\��i�jkr�vH���x���m�wK(�� ���3V�u�M-'� GT��8<����7�sU�O��* ��:9%J�nlv�wc�ӝ|���_��@��8��Jھ�qk+�s�z>oh��GW��4F0k�� �:��7ɞ�E* /�#$� Y⛴P��$I�l�/0�&�D�Ϛ�D�m�� �Uc1@���f��:�;h�`���C�=N�$o*��~������} ��]]Ds'�I �֘���TcᓰҚ�ܐ5�6w�Aނ��~2�²k$Ef��g��� ��ǖ�[ˏ̋��,e �J; �1&�G�h��Pl�d�5d=ƞ��ѓ �Rg�2�� ���2�ٔ��\��RU�7$�Ԭ�4�`d�[��x��K�l̵��eM��UK����~Жc�:Ͳk°����H��l�"g�Vp��D��>�d+��1���n�v�(��Ê�0�hd�>@��ѻs)'G�s���mK�:��� &&rP�A�ǐ�D *�+l�[n ��j3&Y�X1���)��M�smPp��v8�B]6�.�G6�jRN��9E!~��3m�煂�n��;'�\��@3Ō/=~�U��W�_�X~R2Y��� �^�)�vGz9���'v�O&�O1�����V���.��`F�7,������Y��k;i��t��Z�}f_��/ Vyc�]�c�R�U^��� ����R��M���6�/�s�m AD�V+%���e�?�y_��o��z�{�����_��˱y�R@�M�c҆��\�.*`��mVg�/L� �ѿ �6F���0��ݠ�f �i;����P���������`p<�� �G��A���`�\�!O�����+4E�eک�%P�fVmD� l�UM�=�st�v�ހ㮧�ڽ^��7���g������a[����<