github-markdown-preview: unitialized constant GitHub (NameError)

Hiya,

I’m ruby 2.2.0 and I just installed github-markdown-preview using gem install. I got this stacktrace:

/home/wasd/.rubies/ruby-2.2.0/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:69:in `require': uninitialized constant GitHub (NameError)
        from /home/wasd/.rubies/ruby-2.2.0/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:69:in `require'
        from /home/wasd/.gem/ruby/2.2.0/gems/html-pipeline-1.11.0/lib/html/pipeline/markdown_filter.rb:2:in `<top (required)>'
        from /home/wasd/.gem/ruby/2.2.0/gems/github-markdown-preview-3.1.3/lib/github-markdown-preview/html_preview.rb:68:in `pipeline_filters'
        from /home/wasd/.gem/ruby/2.2.0/gems/github-markdown-preview-3.1.3/lib/github-markdown-preview/html_preview.rb:41:in `initialize'
        from /home/wasd/.gem/ruby/2.2.0/gems/github-markdown-preview-3.1.3/bin/github-markdown-preview:31:in `new'
        from /home/wasd/.gem/ruby/2.2.0/gems/github-markdown-preview-3.1.3/bin/github-markdown-preview:31:in `<top (required)>'
        from /home/wasd/.gem/ruby/2.2.0/bin/github-markdown-preview:23:in `load'
        from /home/wasd/.gem/ruby/2.2.0/bin/github-markdown-preview:23:in `<main>'

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 20 (11 by maintainers)

Most upvoted comments

@dmarcotte I ran into this issue, and I believe that I have figured out the root cause.

If rubygems is in a state with zero unresolved dependencies, it just uses the built-in ruby require:

if Gem::Specification.unresolved_deps.empty? then
  RUBYGEMS_ACTIVATION_MONITOR.exit
  return gem_original_require(path)
end

This is the normal state when you boot IRB:

➜ irb
2.2.3 :001 > Gem::Specification.unresolved_deps
 => {}

However, after requiring this gem, unresolved_deps is non-empty. This is because rubygems also considers development dependencies, and does not eager-load everything.

2.2.3 :002 > require 'github-markdown-preview'
 => true 
2.2.3 :003 > Gem::Specification.unresolved_deps.each{ |d| puts d.inspect };0
["json", <Gem::Dependency type=:runtime name="json" requirements=">= 1.7.7, ~> 1.7">]
["thread_safe", <Gem::Dependency type=:runtime name="thread_safe" requirements=">= 0.3.4, ~> 0.1, ~> 0.3">]
["minitest", <Gem::Dependency type=:runtime name="minitest" requirements="~> 5.1">]
 => 0 

This forces rubygems to use the Gem::Specifications to find the file path:

spec = Gem::Specification.stubs.find { |s|
  s.activated? and s.contains_requirable_file? path
}

In general, this should be fine; the gemspecs should contain the correct path information.

However, in November 2014, as part of a performance optimization, rubygems changed the code to use the full file path, instead of the relative path.

begin
  RUBYGEMS_ACTIVATION_MONITOR.exit
  return gem_original_require(spec.to_fullpath(path) || path)
end if spec

Unfortunately, there was a bug in the method to compute the full path:

# inside def to_fullpath(path)
fullpath = nil
suffixes = Gem.suffixes
full_require_paths.find do |dir|
  suffixes.find do |suf|
    File.file? (fullpath = "#{dir}/#{path}#{suf}")
  end
end ? fullpath : nil

Namely, that it doesn’t properly handle cases with C extensions (because they have multiple valid paths).

The github/markdown gem falls into this scenario:

spec.full_require_paths.each do |p|
  Gem.suffixes.each do |s|
    fullpath = "#{p}/github/markdown#{s}"
    puts fullpath if File.file?(fullpath)
  end
end
/Users/davidfeldman/.rvm/gems/ruby-2.2.3/extensions/x86_64-darwin-14/2.2.0-static/github-markdown-0.6.8/github/markdown.bundle
/Users/davidfeldman/.rvm/gems/ruby-2.2.3/gems/github-markdown-0.6.8/lib/github/markdown.rb
/Users/davidfeldman/.rvm/gems/ruby-2.2.3/gems/github-markdown-0.6.8/lib/github/markdown.bundle

So rubygems chooses the first one, which is not actually requirable by itself:

> gem_original_require "/Users/davidfeldman/.rvm/gems/ruby-2.2.3/extensions/x86_64-darwin-14/2.2.0-static/github-markdown-0.6.8/github/markdown.bundle" 
NameError: uninitialized constant GitHub
    from (irb):26:in `require'

And that is where our mysterious error comes from!

Fortunately, this bug was fixed at the end of May 2015:

-      return gem_original_require(spec.to_fullpath(path) || path)
+      return gem_original_require(path)

So this explains the inconsistency: only certain versions of rubygems will experience the issue.

As for solutions, you could just force people to upgrade (or downgrade, I suppose) their rubygems. However, I believe that there is a simpler and more elegant way.

Namely, if you explicitly require github/markdown.rb (as opposed to github/markdown) it will not match on the extension’s .bundle file.

It needs to happen before HTML::Pipeline::MarkdownFilter gets loaded, so I would place the require at the top of lib/github-markdown-preview/html_preview.rb:

require 'listen'
require 'github/markdown.rb'
require 'html/pipeline'