Go ahead!

Memoization for Everything

YAML engine mismatched problem in RubyGems

| Comments

Overview

I hit this problem at fluent-plugin-mongo 0.6.0. Some user failed fluent-plugin-mongo 0.6.0 installation with following message.

1
2
3
# fluent-gem install fluent-plugin-mongo
ERROR:  While executing gem ... (NoMethodError)
    undefined method `call' for nil:NilClass

In short, you should use the range form, e.g.:

1
gem.add_dependency "mongo", [">= 1.5.2", "<= 1.5.2"]

for an exact match dependency for old or Syck environment when building a gem.

Detail

YAML has ‘=’ keyword for default key.

RubyGems uses ‘=’ for an exact match dependency. Current Ruby’s YAML engine is Psych. Psych treat unquoted ‘=’ correctly.

For example, if you add an exact match dependency to the gem:

1
gem.add_dependency "mongo", "= 1.5.2"

then Psych generates following metadata:

1
2
3
4
5
6
7
8
9
10
11
- !ruby/object:Gem::Dependency
  name: mongo
  requirement: &id002 !ruby/object:Gem::Requirement
    none: false
    requirements:
    - - =
      - !ruby/object:Gem::Version
        version: 1.5.2
  type: :runtime
  prerelease: false
  version_requirements: *id002

Problem is here. See line 6.

Old Ruby’s YAML engine is Syck. Syck can’t treat unquoted ‘=’ correctly. In Psych, the result of loading this metadata is correct:

1
"= 1.5.2"

but, Syck parses unquoted ‘=’ as a YAML’s default key by mistake. So loaded result is broken:

1
"#<Syck::DefaultKey:0x0000010380cc40> 1.5.2"

RubyGems don’t know #<Syck::DefaultKey:0x0000010380cc40> operation. In the result, the gem installation failed in Syck environement.

Solution

Use the range form for an exact match dependency

Psych and Syck treat ‘<=’ / ‘>=’ correctly, so we can use following form to avoid this problem.

1
gem.add_dependency "mongo", [">= 1.5.2", "<= 1.5.2"]

This is an ad-hoc approach, but works fine :)
I strongly recommend this approach.

Update RubyGems for user

Latest RubyGems fixed this problem. See repository

Build a gem using Syck

Using Syck, generated metadata is below:

1
2
3
4
5
6
7
8
9
10
11
- !ruby/object:Gem::Dependency
  name: mongo
  requirement: &id002 !ruby/object:Gem::Requirement
    none: false
    requirements:
    - - "="
      - !ruby/object:Gem::Version
        version: 1.5.2
  type: :runtime
  prerelease: false
  version_requirements: *id002

Syck treat ‘”=”’ as a string, not default key. So we can use ‘=’ for an exact match dependency.

If you use Bundler, then put YAML::ENGINE.yamler = "syck" on top of gemspec to enable Syck engine. But some environment doesn’t have Syck engine, so this approch doesn’t work in such environment…

Comments