How to use a gem in Sinatra? - ruby

I'm building a new version of a Facebook app called Lovers. You can find the Lovers source code on GitHub. I'm also building a custom gem for Facebook alongside it. I want the changes that I make to this custom Facebook gem to immediately go into effect for Lovers.
What's the correct way to organize its directory structure? Currently, it looks like so:
|- config.ru
|- features
|- support
|- env.rb
|- lib
|- lovers
|= lovers.rb
|- vendor
| - facebook
| - lib
|- modules/classes here
I added ./vendor/facebook/lib to the $LOAD_PATH from config.ru & env.rb. That's working, but should I be using an init.rb file to do this? What's the best practice?

Instead of creating a vendor folder, I would use Bundler.
Create the Gemfile and define all the requirements.
Then in your lovers.rb file place the following code
require "rubygems"
require "bundler/setup"
Bundler.require(:default, (ENV["RACK_ENV"] || :development).to_sym)
I usually create a boot.rb file in the root folder and move the code there adding lib to the load path.
# boot.rb
$:.unshift(File.dirname(__FILE__) + "/lib")
require "rubygems"
require "bundler/setup"
Bundler.require(:default, (ENV["RACK_ENV"] || :development).to_sym)
# lib/lovers.rb
require "boot"
...

You could put your gem in vendor/facebook, and use Bundler as Simone Carletti suggested, but instead of pulling your gem from rubygems, you could use the :path option, to tell bundler where to load your gem from.
Using that you could even move the gem out of your project's tree while developing them side by side.
Your Gemfile would contain something like:
gem 'facebook', :path => '../facebook'
There are a couple other problems with this.
In order for you to use Bundler to manage your gem, your gem needs a .gemspec file. The gemspec specifies your gem's information, things like version and dependencies. Check out the docs on it.
There is already a gem named facebook, so you might want to think about a different name for your gem.

I think the Behavior Driven Development (BDD) approach would be to build your Facebook gem in a separate source tree and test it with RSpec or some other test harness.
Once the Gem reached a functionally useful iteration, then include it in your application using Bundler as suggested.

Related

Handling require in a project meant to be provided as a gem

I'm completely lost with ruby "require" (and I don't have the option for require_relative since my code is meant to run with ruby 1.8.7.
First some sample code.
requester.rb
require './column/main_column'
....
column/main_column.rb
require './helper' # helper.rb being in the root dir
....
column/segment_column
require './column/main_column'
require './helper' # helper.rb being in the root dir
....
I'm completely lost in how should I use my requires to make my code usable inside a gem and in a standalone way (without assuming a previous gem installation). I was reading at Jekyll's code and every dependencies seems to be loaded in the main rb file which to my opinion is a bit dirty and still do resolve the problem if column/segment_column.rb needs to be called independently.
Please try to argument about different approaches and why one is the best.
You should require files without the extension and relative to your gem project's lib folder.
For example, given this structure:
example-gem/
lib/
example/
gem.rb
example.rb
example-gem.gemspec
You'd require the main example module using:
require 'example'
And in the example.rb file you'd find:
require 'example/gem'
This set of conventions is adopted by the community and is documented on the RubyGems Guides. If they fail, it is often because your gem's lib directory is not in Ruby's $LOAD_PATH, which usually means the gem is not installed.
Building and installing gems during development is cumbersome. For that reason, gems like Bundler became part of the toolboxes of many Rubyists. Bundler lets you use your gem without having to build or install it locally. You can simply bundle exec a script that uses your gem or use bundle console in order to interact with it.

How to use Stylus in a Heroku Sinatra app

I've just started experimenting with Sinatra, deployed to Heroku, and so far I've been able to come to grips with the basics really quickly. Having said that, I'm a bit stuck on how to enable and use Stylus within my application.
Based on what I've read of the Sinatra documentation, this is how I've structured my very basic app.rb file:
require 'sinatra'
require 'stylus'
require 'stylus/tilt'
get '/' do
haml :index
end
In addition, here are the contents of my Gemfile:
source 'https://rubygems.org'
gem 'sinatra', '1.1.0'
gem 'thin'
gem 'haml', '>= 2.2.0'
gem 'stylus'
After using Bundler, and pushing my repo to Heroku it seems as though the necessary goodness is happening – console output shows that stylus, stylus-source and execjs (which I'm assuming is responsible for compiling the .styl files) are being included correctly.
My questions then are:
a) Where would the .styl files live? My current folder structure is:
app -
Gemfile
Gemfile.lock
Procfile
app.rb
public -
stylesheets
views -
layout.haml
index.haml
b) How do I compile/reference the .styl files? Linked in the head, referenced in the route, or ...?
a) I put any stylesheets (uncompiled) into app/views/stylesheets/.
b) You can either precompile them into app/public/css and then reference them the way you would reference any other external CSS, with a link in the head (which is how I do things) or, you can use the way you've set up for, by compiling via a route call, e.g.
get "/screen.css" do
stylus :"stylesheets/screen"
end
You will probably want to add in some kind of caching with this.

Bundler: why does it read the gemspec on require "bundler/setup"?

The title is the question, and here's the context that prompts it.
The Gemfile:
source "http://rubygems.org"
# Specify your gem's dependencies in the gemspec
gemspec
Here is the top of the rackup file:
require 'rubygems'
require "bundler/setup"
On running the rackup file an error is thrown:
<module:Rack>': GemName is not a class (TypeError)
Why? Because I'm writing a piece of Rack middleware, and the standard layout is:
lib/
rack/
gem_name.rb
gem_name/
version.rb
gem_name.rb will contain:
module Rack
class GemName
version.rb will contain:
module Rack
module GemName
VERSION = "0.0.1"
Finally, the gem_name.gemspec will contain:
require "rack/flash-in-the-pan/version"
#...
s.version = Rack::GemName::VERSION
Naming a module and a class by the same name isn't a problem as long as you don't require both files at the same time. Normally, this wouldn't happen, as you either need the version for building the gem, or you need to run the gem library, only one or other gets required.
But, this time I decided to use Bundler to manage the gem's dependencies. When requiring the gem library via bundler it obviously runs the gemspec too. I can "fix" it easily enough, I define the version number by hand in the gemspec.
So back to my question - why does Bundler need to look in the gemspec at the library's runtime?
bundler (1.0.21)
Any insight is much appreciated.
Whenever you run bundler, it has to parse the Gemfile to actually figure out what gems need to be loaded, what has to be added to $LOAD_PATH and so on. As part of that, it has to parse gemspec.
The Gemfile.lock contains info on all of the gems as well as the dependencies to save startup time, but it doesn't alleviate the need for it to parse the Gemfile.
There are various ways you could work around it. Two simple ones would be to use File.read and some regex to pull out the version. Or require the gem_name.rb and gem_name/version.rb files.

Add the gem I'm developing to be loaded automatically by rubygems without build/install?

I'm developing a gem with Jeweler in a custom directory.
I want to be able to require the gem within any app (and also the executables files from $PATH), and without needing to build and install the gem each time I modify it.
I thought about 2 ways:
I make a symlink to $GEM_HOME/gems and $GEM_HOME/bin
I add the bin directory to $PATH and the lib directory to rubygems to be loaded.
But I bet there is a proper way to do this.
You can specify a local path in the gem command:
gem 'your-gem', '1.2.3', :path => 'path/to/your-gem'
Update: As #Nick points out in the comments,
This is specific to using bundler. In general, it's just require '/path/to/your-gem.
I'd like to add, however, that if you're using a custom-developed gem, bundle will probably make your life easier if you're not already using it. This is because with bundler, when you're done developing the gem (or at a stable/release point) you can load a gem directly from a github repository like this:
gem 'your-gem', :git => 'git#github.com:you/your-gem.git'
No need to mess around with your Gemfile
require 'bundler/setup' will put the right gem into your $LOAD_PATH and allow you to require it on the next line.
#!/usr/bin/env ruby
require 'bundler/setup'
require '<gem-name>'

User-level bundler Gemfile

I'd love to have a Gemfile in Bundler that just sets my own personal Gemfiles to always be bult into bundles...
aka ruby-debug, interactive-editor, and so forth.
Any idea how to do this?
We use this technique.
Puth this in your Gemfile:
eval File.read(File.expand_path("Gemfile.personal")) if File.exists?(File.expand_path("Gemfile.personal"))
And then add your personal gems to Gemfile.personal. Of course exclude Gemfile.personal from your version control.
One way to do this is to create different evnironments
group :scott do
end
Then
bundle --with-env=scott
I'm not 100% sure what it is you are trying to achieve, but;
If you just want to specify a number of development-only gems, you can specify a development group that can be excluded from deployments:
group :development do
gem "ruby-debug"
gem "interactive-editor"
end
Then on production or test you would do:
bundle install --without development
The cleanest solution I found so far is to use a separate Gemfile.personal and use a custom Gemfile path. I like this solution because you can use it in any project without modifying project code at all.
1. Add Gemfile.personal in to project root dir
# /path/to/your_ruby_project/Gemfile.personal
eval File.read('Gemfile') # use all gems from Gemfile
gem 'personal-gem1'
gem 'personal-gem2'
2. Install gems using Gemfile.personal file
BUNDLE_GEMFILE="Gemfile.personal" bundle install
# or
bundle install --gemfile=Gemfile.personal
Just remember to specify BUNDLE_GEMFILE every time you execute commands with bundler.
I personally put BUNDLE_GEMFILE=Gemfile.personal env variable in .env file using dotenv which ensures that Gemfile.personal is always used when I execute any command with bundler so I do not need to put it manually every time.
3. Put Gemfile.personal and Gemfile.personal.lock to .gitignore
For linux users:
touch ~/.gitignore
echo "Gemfile.personal\nGemfile.personal.lock" >> ~/.gitignore
This will affect all projects, so you do not need to update each project .gitignore separately.
My proposition does not depend on Bundler. As such does not clutter Gemfile* with your private gems for the price being a bit less convenient than answer by #ScottSchulthess.
How Bundler works
There is an array stored in $LOAD_PATH global variable which is a "load path for scripts and binary modules by load or require" (see Ruby docs) and Bundler modifies this array.
If you're developing a gem, $LOAD_PATH it will contain paths to all gems in the system. You can simply write e.g. require "pry" somewhere and pry gem will be loaded properly even if it's not mentioned in gemspec nor Gemfile. You don't have to add it to dependencies. (Of course it has to be already installed with gem install pry.)
A very different strategy Bundler takes when you're developing an application. In such case most of $LOAD_PATH will be removed on require bundler/setup (Rails calls it in config/boot.rb). Only essential paths and those pointing to gems specified in Gemfile.lock will remain there. So if you want to use pry without adding it to Gemfile, you got to append it to $LOAD_PATH before requiring it.
Solution for applications
gems_root = $LOAD_PATH.detect{ |p| %r{/bundler-} =~ p}.sub(%r{/bundler-.*}, "")
additional_gems = {
"pry" => "pry-0.10.1/lib",
"pry-rails" => "pry-rails-0.3.2/lib",
}
load_paths = additional_gems.values.map{ |p| File.join gems_root, p }
$LOAD_PATH.unshift *load_paths
additional_gems.keys.each{ |r| require r }
If you're using Rails, then save it in /config/initializers/00_custom_gems.rb and that's all. Outside Rails you additionally need to require it, preferably right after require "bundler/setup":
require "path/to/it" if File.exists? "path/to/it"
Remember to mention this file in .gitignore.
Sometimes proper gem path does not end with /lib but with architecture name. The easiest way to learn it is to add it for a moment to Gemfile and do puts $LOAD_PATH in aforementioned initializer. You can also learn those dirs from gemspec.
Solution for gems
When developing gem, you don't need to enhance $LOAD_PATH, only to require gems you want. If you need custom gems in tests and you're using RSpec, it can be done somewhere in /spec/support.
Another (less sane) idea is to add a file lib/development.rb:
require_relative "my_gem_name"
require "path/to/private/requires" if File.exists? "path/to/private/requires"
and refer to this file instead of to "my_gem_name" in your tests, demo application etc..

Resources