How does require 'digest/md5' works? - ruby

require 'digest/md5' # => true
Digest::Md5.hexdigest('') # => "d41d8cd98f00b204e9800998ecf8427e"
Which file does this require use? Is an actual file being required? Or is it required programmatically?

This is (sorta) explained in the documentation for Kernel#require:
require(name) → true or false
Loads the given name, returning true if successful and false if
the feature is already loaded.
If the filename does not resolve to an absolute path, it will be
searched for in the directories listed in $LOAD_PATH ($:).
If the filename has the extension “.rb”, it is loaded as a source file; if
the extension is “.so”, “.o”, or “.dll”, or the default shared library
extension on the current platform, Ruby loads the shared library as a Ruby
extension. Otherwise, Ruby tries adding “.rb”, “.so”, and so on to the
name until found. If the file named cannot be found, a LoadError will be raised.
By default $LOAD_PATH will include the directories where Ruby's standard library lives. Directories, plural, because native (compiled binary) modules in the standard library live in a different directory than pure-Ruby modules.
Digest is actually a great example because some of its files are native and others are Ruby.
On my Mac $LOAD_PATH includes (among others) these two directories (truncated for readability):
.../usr/lib/ruby/2.0.0
.../usr/lib/ruby/2.0.0/universal-darwin15
The former is where pure-Ruby modules live and the latter is where native modules live.
So when I do require 'digest/md5' Ruby first looks in .../usr/lib/ruby/2.0.0/ for a directory named digest/, and upon finding it, for a file in it named md5.rb. It doesn't find it, so it looks for md5.bundle. Why .bundle and not .so or .o? Because that's the "default shared library extension" that Ruby was configured with when it was compiled for OS X. Go figure.
Ruby doesn't find it there, so next it looks in .../usr/lib/ruby/2.0.0/universal-darwin15/digest/. There's no md5.rb there, but there is an md5.bundle, so Ruby loads it.

It loads a C-extension that can be found in Ruby's source dir - ruby/ext/digest/md5/

It's one of Ruby's standard libraries (stdlib), which means that it's available in pretty much every implementation of Ruby. It does, in fact, load a file. But it's a file that was copied to your computer when you installed Ruby and is pretty much always going to be available to you. Another good example of a standard library is DateTime.
One thing that confused me a lot when I started learning Ruby and Rails was that Rails requires a fair number of Ruby's standard libraries for you. So it's good to be aware of what's actually going on under the hood.

Related

What exactly is a gem native extension?

I feel that a native extension is like libraries that you should install onto your system before trying to install those gems, which depend on the native extensions. Like the ImageMagic library. Is that correct? Is there something else that we should know about native extensions?
A gem native extension might link to a separate library that needs to be pre-installed, and RMagick is an example of that.
However, the term really just means "includes some code that needs to be compiled for your machine". The code is compiled and linked so that the resulting executable file can be required by Ruby on the machine.
The usual reasons for writing a Ruby C or C++ extension are:
Speed. For some CPU-intense tasks, C code can be 100 times faster than Ruby. In this case a native extension can be completely stand-alone with all C source code included in the gem.
Third-party library already written in C. In this case the gem will have C source code that binds the library functions into Ruby modules, classes and methods.
You can view the C source code for gems with native extensions, it is installed alongside the Ruby source code. By convention, there is a folder inside the gem called ext/gem_name which contains a Ruby file extconf.rb that passes information to the compiler (technically it creates a make file). In addition the C source files are put there as well.
MRI Ruby is implemented as a very "flat" structure in C, basically consisting of a large number of C functions. This makes it relatively easy to learn how to implement a native extension, even if you don't know much C. You can read Extending Ruby 1.9 as an introduction to the topic.
Native extensions can fail to install or work properly. There are many questions on Stack Overflow asking for help with specific failed installs. The usual problems are:
Missing libraries. Hopefully the gem author will explain what you need to pre-install in the README, but is not always clear.
Compiler mismatches. It is difficult to test all target systems, so sometimes the instructions in extconf.rb won't work on a specific system, or the C code will raise warnings or not compile due to differences. In Windows, you probably won't have a working compiler unless you install the Ruby Devkit
Will not work with all versions of Ruby. For instance, JRuby can use C native extensions, if it has been enabled but it is not always advisable - the topic is quite complex, but generally avoid mixing JRuby and native extensions.
Native extension is just a gem which is written (entirely or partially) in C.
It may or may not depend on an external library, this is not a factor here. What matters is that such gem needs to be compiled and it is likely to be platform-dependant (there was a reason to use C, right? Maybe for using some low-level OS API or something. But most often it's to interface with a library).
Quoting this article
“Native extensions” are the glue that connects a Ruby gem with some other non-Ruby software component or library present on your machine.
The native extensionis not the dependency. A native extension is generally a C code that interacts with a non-Ruby dependency.
For instance, a gem that uses ImageMagic have a native extension written in C that talks to ImageMagic and represents the bridge from the Ruby gem to ImageMagic.
When you install the gem and the native extension is compiled, you don't compile the C library (e.g ImageMagic), that library must be already present on your system. You compile the C bridge bundled with the gem.
I'm not big ruby specialist, so take this with a grain of salt:
I'm fairly certain that it's just a gem that needs to install a native (e.g. C-Library) library in order to work. A lot of gems simply wrap existing C-libraries with a Ruby API.
The installation of the gem will trigger the download of C-libraries, which will then be built using gcc or another compiler. If your system configuration is not supported, you'll need to pass parameters to gem tool to indicate the right directories, etc.. if you're unlucky you might need to change the make files directly.

Ruby where is etc module file and can it be customized?

Etc module is great, but if I used NIS or LDAP where passwd, group, and shadow are not from /etc/{passwd, group, shadow}, then Etc module is not going to help.
Is it possible to edit the Etc module path's /etc/{passwd,group,shadow}?
On my Linux box, I have gone into /usr/lib/ruby/1.8, there are modules but couldn't fine etc.rb or related modules.
Many thanks
In general, you can use
SomeClass.method(:foo).source_location
to find out where (on disk) a method is defined. I don't think this was available in ruby 1.8, but even it is the result is nil, because these methods are implemented in C. The ruby 1.9 implementation is here for example. Somewhere in /usr/lib/ruby/1.8 there should be an etc.so (on linux or etc.bundle on os x and so on)
This doesn't mean that you can't overwrite the method, but it does mean that you can't just edit the source as you would with a plain .rb file (you'd have to recompile the extension afterwards and move it to the correct location, which is system dependant)

When writing a gem, how bad practice is to include its module by default?

When writing a Ruby library, when is it acceptable practice to do this in one file?
module MyLib
# some definitions
end
include MyLib
I found that usually, one has to
require 'some_gem'
first, and then
include SomeGem
But I wonder, in some simpler cases, when you just want to add a bit of funcionality to the core, would it be O.K. to include the main module by default?
The require 'some_gem' / include SomeGem combo is something found mostly in scripts (as opposed to libraries). It is important, in libraries, to keep the namespaces separate -- this is the whole point of having namespaces in the first place.
Yes, it is a bad practice. If you include it, then you are choosing how people can use it. You are making the decision for them that they want it included in the global namespace. That isn't your decision to make, be a good Ruby citizen, don't change your user's environments. Allow them to choose how they want to use the code.

What are the ruby conventions for $LOAD_PATH for non-gems?

I have some code in a ruby project. From what I understand, library code should live in the lib folder.
so I have
/lib
/mymodule.rb
/mymodule
/somefile.rb
I have been using this internal library by calling require 'relative/path/to/lib/mymodule.rb'. What is the best practice to all this library code to the $LOAD_PATH? All I want to do is say
require 'mymodule'
instead of requiring the file directly.
I have been googling around for the last 30mins, and it is not obvious to be what the best practice/convention is.
You can either use require_relative, which might be a bit messy if
you move files around and therefore slightly unstable. Or set
$LOAD_PATH in a startup/init file of your project. Just be aware of
using your own subfolder in that case to avoid name conflicts with
your gems.

IronRuby - how to require .NET assemblies without strong name?

This page on the IronRuby help website talks about being able to 'require' some well-known assemblies such as System.Windows.Forms without needing to crank out the entire 'ah-come-on-gimme-a-break-here-you-cannot-be-serious' strong name of the assembly.
In the docs it says this:
>>> require "System.Windows.Forms"
=> true
But when I try the same 'require', I get this:
>>> require "System.Windows.Forms"
IronRuby.Libraries:0:in 'require': no such file to load -- System.Windows.Forms (LoadError)
from :0:in 'Initialize##1'
What might I be doing wrong? Could it be a setup problem? I can't see this "libs directory on the load path" that gets mentioned in the documentation. Is the documentation wrong?
Thanks.
The way that this works is because the IronRuby guys have written a bunch of wrapper scripts.
Look in <your ironruby install path>\lib\ironruby and you'll see System.Windows.Forms.rb, System.Drawing.rb etc.
What happens when you do require 'System.Windows.Forms' is that IronRuby finds that rb file and runs it. The code inside that file just does the full strong-named require for you.
If you want to load other dll's that they haven't written wrappers for, you have 3 options:
require the full path to the dll (eg c:\program files\whatever\whatever\blah.dll)
require the strong name (this only works if it's in the GAC or somewhere else IronRuby can find dll's in)
use load_assembly - This is the most convenient, but IIRC it loads the dll into the LoadFrom context, not the Load context.
If you don't understand what that means, then basically it's fine in testing, but don't do it in production :-)
Well, it was a setup problem - there were two copies of ir.exe in the IronRuby download, and I was using the wrong one.

Resources