info | twitter | github
J-_-L | Writings on Ruby Programming
01.09.10 02.09.10
9 comments

How to properly check for your Ruby interpreter, version and OS

gem · hints · ruby · tutorial · zucker

Zucker 4 adds accessors to some environment information:

  • OS: returns the current operating system
  • RubyEngine: returns the current Ruby implementation
  • RubyVersion: returns the current Ruby version

And here is how it works.

OS

The basic way to get the operating system from a Ruby script is the RUBY_PLATFORM constant. But it’s not recommended, because some Ruby implementations report the virtual machine on which they run (e.g. java). A simple solution is the RbConfig::CONFIG hash, which is build when Ruby is build.

require 'rbconfig'; RbConfig::CONFIG['host_os']

Let’s abstract this information to build a helpful OS constant:

Listing 1
/35/os.rb ruby
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
require 'rbconfig'

module OS
  class << self
    def is?(what)
      what === RbConfig::CONFIG['host_os']
    end
    alias is is?

    def to_s
      RbConfig::CONFIG['host_os']
    end
  end

  module_function

  def linux?
    OS.is? /linux|cygwin/
  end

  def mac?
    OS.is? /mac|darwin/
  end

  def bsd?
    OS.is? /bsd/
  end

  def windows?
    OS.is? /mswin|win|mingw/
  end

  def solaris?
    OS.is? /solaris|sunos/
  end

  def posix?
    linux? or mac? or bsd? or solaris? or Process.respond_to?(:fork)
  end

  #def symbian?
    #TODO who knows what symbian returns?
  #end

  # ...
end

Because of the module_function method, you can either call the methods on the module or include the module to call them without prefix.

RubyEngine

Most Ruby implementations set the RUBY_ENGINE constant to identify themselves, but not all – for example, the official Ruby 1.8 does not have one. This snippet takes care of some exceptions:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
module RubyEngine
  class << self
    # try to guess it
    @interpreter = case
    when RUBY_PLATFORM == 'parrot'
      'cardinal'
    when Object.constants.include?( :RUBY_ENGINE ) ||
         Object.constants.include?( 'RUBY_ENGINE'  )
      if RUBY_ENGINE == 'ruby'
        if RUBY_DESCRIPTION =~ /Enterprise/
          'ree'
        else
          'mri'
        end
      else
        RUBY_ENGINE.to_s # jruby, rbx, ironruby, macruby, etc.
      end
    else # probably 1.8
      'mri'
    end

    def is?(what)
      what === @interpreter
    end
    alias is is?

    def to_s
      @interpreter
    end
  end

module_function

  def mri?
    RubyEngine.is? 'mri'
  end
  alias official_ruby? mri?
  alias ruby? mri?

  def jruby?
    RubyEngine.is? 'jruby'
  end
  alias java? jruby?

  def rubinius?
    RubyEngine.is? 'rbx'
  end
  alias rbx? rubinius?

  def ree?
    RubyEngine.is? 'ree'
  end
  alias enterprise? ree?

  def ironruby?
    RubyEngine.is? 'ironruby'
  end
  alias iron_ruby? ironruby?

  def cardinal?
    RubyEngine.is? 'cardinal'
  end
  alias parrot? cardinal?
  alias perl? cardinal?
end

RubyVersion

The used Ruby version can be accessed with RUBY_VERSION. To simplify version checking, this snippet adds some methods for querying and the possibility to check for 1.8 / 1.9 using a Float:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
### usage examples
# RubyVersion
### check for the main version with a Float
# RubyVersion.is? 1.8
### use strings for exacter checking
# RubyVersion.is.above '1.8.7'
# RubyVersion.is.at_least '1.8.7' # or below, at_most, not
### you can use the common comparison operators
# RubyVersion >= '1.8.7'
# RubyVersion.is.between? '1.8.6', '1.8.7'
### relase date checks
# RubyVersion.is.older_than Date.today
# RubyVersion.is.newer_than '2009-08-19'
### accessors
# RubyVersion.major # e.g. => 1
# RubyVersion.minor # e.g. => 8
# RubyVersion.tiny  # e.g. => 7
# RubyVersion.patchlevel # e.g. => 249
# RubyVersion.description # e.g. => "ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux]"

require 'date'
require 'time'

module RubyVersion
  class << self
    def to_s
      RUBY_VERSION
    end

    # comparable
    def <=>(other)
      value = case other
        when Integer
          RUBY_VERSION.to_i
        when Float
          RUBY_VERSION.to_f
        when String
          RUBY_VERSION
        when Date,Time
          other.class.parse(RUBY_RELEASE_DATE)
        else 
          other = other.to_s
          RUBY_VERSION
        end  
      value <=> other
    end  
    include Comparable

    # chaining for dsl-like language
    def is?(other = nil)
      if other
        RubyVersion == other
      else
        RubyVersion
      end
    end
    alias is is?

    # aliases
    alias below     <
    alias below?    <
    alias at_most   <=
    alias at_most?  <=
    alias above     >
    alias above?    >
    alias at_least  >=
    alias at_least? >=
    alias exactly   ==
    alias exactly?  ==
    def not(other)
      self != other
    end
    alias not?     not
    alias between between?

    # compare dates
    def newer_than(other)
      if other.is_a? Date or other.is_a? Time
        RubyVersion > other
      else
        RUBY_RELEASE_DATE > other.to_s
      end
    end
    alias newer_than? newer_than

    def older_than(other)
      if other.is_a? Date or other.is_a? Time
        RubyVersion < other
      else
        RUBY_RELEASE_DATE < other.to_s
      end
    end
    alias older_than? older_than

    def released_today
      RubyVersion.date == Date.today
    end
    alias released_today? released_today

    # accessors

    def major
      RUBY_VERSION.to_i
    end
    alias main major

    def minor
      RUBY_VERSION.split('.')[1].to_i
    end
    alias mini minor

    def tiny
      RUBY_VERSION.split('.')[2].to_i
    end
    
    alias teeny tiny

    def patchlevel
      RUBY_PATCHLEVEL
    end

    def platform
      RUBY_PLATFORM
    end

    def release_date
      Date.parse RUBY_RELEASE_DATE
    end
    alias date release_date

    def description
      RUBY_DESCRIPTION
    end
  end
end

Bugfixes are welcome ;) Update: new RubyVersion implementation (thanks to Hanmac for the hint)

Creative Commons License

Comments

02.09.10

random

Nice colors on the code syntax. what's the theme called?

02.09.10

J-_-L

Hi random, It's hand crafted (inspired by railscasts), see the css for the source ;)

03.09.10

trans

Looks like a bug in RubyEngine, it can return a symbol but #is? compares a string.

Also, here's an idea... extend the actual constants with your methods. e.g. Get rid of the `class << self` and then `RUBY_VERSION.extend(RubyVersion)`. Or just do `class << RUBY_VERSION`. Then we can can do `RUBY_VERSION.major`, etc.

03.09.10

J-_-L

Hi trans,
thank you for your interest and thanks for spotting the bug :).

About the idea: It's very interesting. I've tried it, but noticed that I had to recreate RUBY_VERSION with <code>Object.send :remove_const, :RUBY_VERSION</code>, because it's frozen. That might not be a problem, but I think, I stick to the extra constant. One expects, that it offers extra methods, because of the slightly different name. However, from RUBY_VERSION, most people expect it to be a normal string.

23.12.11

sampablokuper

Hi Jan,
I see you've made os.rb available under CC-BY. I'd be really grateful if you'd make it available under a GPL-compatible license too! Thanks,
Sam

04.01.12

J-_-L

Hi sampablokuper, you can use it under the terms of the gpl version 3. :)

30.05.12

dbirtwell

Seems like there might be a bug under Mac OS X. The following

puts "Is Mac: #{OS::mac?}"

returns true. Probably because "Darwin" contains "win"

30.05.12

dbirtwell

Sorry, that should be

puts "Is Windows: #{OS::windows?}"

returns true under Mac OS X

09.08.12

Joseph

@dbirtwell try this fix:

def windows?
OS.is? /mswin|^win|mingw/
end