What's the best practices in code reuse between different Ruby projects? - ruby

guys!
I'm a software developer with Java background and I'm starting some projects using a Ruby web framework (Padrino/Sinatra).
In my java projects, I usually had some "common" projects whose classes where used in several projects. For instance, I had a central authentication service, and a shared database that stored the user profiles. All my projects that used this service shared some models mapped to the user profile database.
So, despite the framework, orm lib etc., what's the best way of sharing code across several Ruby projects?

Besides this, ruby's gems is one of the best way of reusing common parts of code. Gems have names, version numbers, and descriptions and therefore you can easily maintain up-to-date versions of these libraries, install and uninstall, manage your computer’s local installations of gems using the gem command, available from the command line. Gems became standard with Ruby 1.9, but before you have to use require 'rubygems' line in the scripts. There are several tools that help create them, for example, bundler. Bundler not only tool for gem creation, but an awesome application's dependencies manager.

Put your code into a something.rb file and require it at the top of your other script(s).
You can also use load instead of require, but require has the nice property that it will not include a file more than once. Also, using load requires the .rb extension, whereas require does not, i.e.,
#some_script.rb
puts('hello world')
#another script
require 'some_script'
>> hello world
load 'some_script'
LoadError: no such file to load -- some_script
from (irb):2:in 'load'
from (irb):2
You will almost always use require, but load is an option as well if you want to use it for... whatever reason.

Related

Why the Ruby Gems code have certain file that just requires file with same name but the only difference is they have snakecase?

For our current question, let take this repo as an example.
https://github.com/slack-ruby/slack-ruby-client/tree/master/lib
In this repo, we can see the following list of files.
The slack_ruby_client.rb just has require 'slack-ruby-client' in the file. I am seeing similar convention in few other gems as well. Another example is https://github.com/orbit-love/notion-ruby-client/tree/main/lib
Is there something with the logic in here? Can someone please explain me or direct me to any article that talks about this?
Because a lot of programmers don't understand that slack_ruby_client and slack-ruby-client are not the same thing, and so the authors of the gem provide both files to avoid being flooded with bug reports.
The authors of Nokogiri did the same thing because programmers kept mis-spelling it as require 'nokogirl' instead of require 'nokogiri' and reporting bugs that it is not working, so the Nokogiri authors just put in a file named nokogirl.rb which just contains require 'nokogiri'.
At first, the default entry point of a gem is commonly the exact same file as the name of the gem, as this follows the hardcoded defaults of bundler. There is also a potential conflict of conventions used in naming gems, ruby files and the mapping to constants (classes, modules, ...) in those files.
At first, when adding a gem as a dependency to your Gemfile, bundler may try to automatically require the gem. Here, it uses the name of the gem dependency by default (this can be overridden with the :require option).
Because of this, the normal entry point for the slack-ruby-client and notion-ruby-client gems is named the same as the gem. Bundler is thus able to require the gem code without any further instructions.
Now, there are other ways to require Ruby code than to just rely on bundler autorequiring. Often, developers manually require code to ensure that their dependencies are guaranteed to be loaded, regardless of whether bundler is used or not.
The usual convention when naming ruby code files is to use snake_case names named after the respective class / module defined in the file. A class names MySpecialClass would be defined in a file named my_special_class.rb
Given that the gem is called slack-ruby-client and people being aware of this specific name, they might thus assume that they can or should require the gem as require "slack_ruby_client", assuming that snake_case file names are used here (as would be true most of the case in Ruby). The slack-ruby-client gem supports this assumption by providing an appropriately named file which loads the actual module.
Additionally, when manually requiring code, many people assume that they can just require the desired module using its module name converted to snake_case. In the case of the slack-ruby-client which provides a Slack module, people may thus assume that they can just use require "slack" to load the module.
Accordingly, the gem also provides a file called slack.rb which requires the actual entry point for the gem.
In the end, those files are added by gem authors to provide convenience entry points for people assuming various conventions for naming gems and / or files in them in order to provide the correct functionality seamlessly.

How do I create a config.ru file and a Rakefile?

I know you create a Gemfile from the command line by typing "bundle init." But how do you create config.ru file and a Rakefile?
Kristine, the real question is not how, but rather why would you want to create them.
Those three files serve three different purposes, and none are required for a Ruby application to get started:
Gemfile (and its companion Gemfile.lock that gets generated by the first bundle install and should be kept as safely as the other one) – this is one you meet most often.
It belongs and is used by a tool called Bundler. It's a dependency management tool. When your application needs some other library called "gem", you can list it in your Gemfile, do bundle install and later on when you run your application like bundle exec ruby yourapp.rb Bundler will take care of the environment in such a way, that your application always gets the same versions of gems that you have designed it to get (those versions are actually stored in Gemfile.lock file, you can peek there).
You can easily do without Bundler, but it usually makes sense to stick to certain gem versions. That's why people usually use it. I would highly suggest you taking a look at the tool's site.
config.ru – this is very common for web applications. It's a Rack configuration file. There's a widely spread in Ruby world web server API called Rack. It enables decoupling web applications (like your Rails app, or Sinatra app) from the underlying web application server (like Thin, Unicorn or WEBrick).
Though you can certainly create this on your own, you most certainly don't need to. It's been a long way in my Ruby/Rails experience before I had to actually do this. Usually this file gets bootstrapped when you create a new Rails app by calling rails new.
And vanilla command line Ruby apps just don't need it.
Rakefile – this is, again, a pretty wide-spread beast. What Makefile is for make, Rakefile is for rake. Rake is a Ruby tool to describe and invoke certain tasks from a command line. For example, when you do bundle exec rake db:migrate, you actually start a task described by a Rakefile.
You can easily design your own tasks, but as you start using Rails you usually don't need to. rails new drops a Rakefile for you that's just enough to start with, and unless you're doing some really custom (that should, exempli gratia, involve calling your Rails app code from the command line), there's no necessity in fiddling with it.
Needless to say, if you're doing some simple Ruby console app that just asks your name and greets you, you don't need this file either.
Hope this helps you getting your head around this and smooths your ride into the Rails world!

Using packages in Ruby?

I just started learning Ruby coming from Java. In Java you would use packages for a bigger projects. Is there anything equivalent to that in Ruby? Or what is the best way to achieve a package like setting?
The way I'm doing it right now is 'load'ing all the needed class into my new Ruby file. Isn't there a way to tell my current Ruby class to use all other Ruby classes in the same folder?
Cheers,
Mike
There's three kinds of package loading in Ruby:
Explicitly loading a library with require
Implicitly loading a library using autoload
Importing a library with gem
The require method is the most direct and has the effect of loading in and executing that particular file. Since that file may go on to require others, as a matter of convenience, you may end up loading in quite a lot at once.
The autoload method declares a module that will be loaded if you reference a given symbol. This is a common method to avoid loading things that you don't need, but making them automatically available if you do. Most large libraries use this method of loading to avoid dumping every single class into memory at once.
The gem approach is a more formalized way of packaging up a library. Although it is uncommon for applications to be split up into one or more gems, it is possible and provides some advantages. There's no obligation to publish a gem as open-source, you can keep it private and distribute it through your own channels, either a private web site or git repository, for instance, or simply copy and install the .gem file as required.
That being said, if you want to make a library that automatically loads a bunch of things, you might take this approach:
# lib/example.rb
Dir.glob(File.expand_path('example/**/*.rb', File.dirname(__FILE__))).each do |file|
require file
end
This would load all the .rb files in lib/example when you call require 'example'.
You probably want to use require rather than load, since it should take care of circular references.
If you want to grab all the files in a given folder, that's easy enough:
Dir.foreach("lib"){|x| require x}
Your other option is to have a file that manually requires everything, and have your other files require that.
You should also look at wrapping the code in your libraries with a module block, to give them their own namespaces.
That said: rightly or wrongly, I tend to feel that this is the one area -- perhaps the only one -- where Ruby is less powerful than Python, say, or Java.
I understand your feeling. It's an ordinary problem you have to face when coming from another language like Java. I'd say try to study Ruby modules but you deserve a longer reply. So my advice is reading a good Ruby book like Eloquent Ruby.

nested rails gems and gem management

Rails is collection of several gems, all who's source resides in the rails repository, aka active_record has its own gemspec but is at github.com/rails/rails/active_record. While I use this stuff all the time, I dont really know the details of creating my own gems using this strategy. I have a project for work where certain codebases will be reused from project to project, and think that I would like to build gems for each recurring toolset. While the gems will always be used in a namespace, eg Company::LegacyRecord I think it would be ideal to have LegacyRecord, in this case be its own gem. What would be the rationale for building a collection of gems with a toplevel namespace, like Company, in my example, or rails as the top level namespace, versus gems that are totally independent?
you probably only need to do that for very large projects. The benefit is that you can "split up the functionality" across gems and each one is more specialized.

Gotchas for writing rubygems

There have been questions with answers on how to write rubygems, but what should you avoid in writing a rubygem? What can cause problems for people using your rubygem?
Gem Packaging: Best Practices gives a lot of advice, some of which include
Don't pollute the global load path. Ideally, only have foo.rb in your lib directory, and put all your other files in lib/foo.
Don't require files using __FILE__.
Don't rely on anything outside the load path. Folders may not have the same structure as in your original version. For example, don't use something like
VERSION = ::File.read(::File.join(::File.dirname(FILE), "..", "..", "VERSION")).strip
Don't manage $LOAD_PATH within lib.
Provide a VERSION constant.
Don't depend on rubygems. The person using your code may not be using rubygems, but some other packaging system (or no packaging system). Similarly, don't mention version dependencies in the code itself, or rescue Gem::LoadError.
Rubygems dependencies. Please... argues that you shouldn't list optional runtime dependencies, and should separate developer from runtime dependencies.
From my own experience: if nothing else, try building and installing your gem locally before releasing it into the wild. It avoids brown paper bag releases.

Resources