Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run a Rails application within a gem?

I'm not sure if this sort of thing is very common, but I keep finding myself trying to create gems that are just wrappers around a Rails application.

My gem will have a generator to create a config.ru but the Rails application will live inside the gem's lib directory. I need to know how to "embed" a Rails application and configure it so that it can be run inside the gem.

For example:

$ mygem new project

mygem created a directory called "project" with the following files:

project/config.ru
project/widgets/
project/foobars/

My gem will also generate some directories that will need to be added to Rails somehow so that I can access the code in those directories from the Rails app living inside the Gem.

Any help or advice you can give me would be appreciated.

To clarify, I'm not trying to create a Rails engine, or plugin to a Rails application. I'm trying to create a fully-fledged Rails application, but package it as a gem so that a user of my gem can run the gem (the rails app) without needing to know that it's using Rails behind the scenes.

Update: Okay, I've got a little bit working now. I've created the gem and generated the rails project inside the gem's lib directory.

$ bundle gem my_gem && cd my_gem/lib
$ rails new my_gem --skip-bundle

Which leaves me with:

my_gem/
  my_gem.gemspec
  bin/my_gem
  lib/
    my_gem.rb
    my_gem/
      version.rb # generated by bundler
      # the rails app:
      app/
      config/
      Gemfile
      ...etc

Since this gem requires Rails, I started adding the gems defined in the Rails Gemfile as dependencies in the gem's Gemspec, but I'm a little confused as to how to handle the assets group in the Gemfile.

# Rails Gemfile
group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'
  gem 'therubyracer', :platforms => :ruby
  gem 'uglifier', '>= 1.0.3'
end

# gemspec
Gem::Specification.new do |gem|
  gem.name          = "my_gem"
  # ...
  gem.add_dependency 'rails', '3.2.8'
  gem.add_dependency 'sqlite3'
  gem.add_dependency 'jquery-rails'
  # how to add the assets group gems?
end
like image 206
Andrew Avatar asked Aug 20 '12 14:08

Andrew


1 Answers

Try this and see if it helps you make progress.

Gems are just directories of files, and you can put whatever files you want into a gem.

Create:

Create a blank gem full-blown Rails project:

$ bundle gem my_gem

Then a Rails app:

$ rails new my_app --skip-bundle

Copy the Rails files into the gem:

$ cp -R my_app/* my_gem

Bundle everything into your Rails app:

$ cd my_gem
$ bundle install --binstubs --path vendor/bundle
$ cd -

Make the Rakefile have the gem tasks and the Rails setup:

#!/usr/bin/env rake
require "bundler/gem_tasks"
require File.expand_path('../config/application', __FILE__)
MyApp::Application.load_tasks

Verify that it starts:

$ rails server

Load Path:

To control where Rails looks for files, such as "external" configuration files, you can use the file config/application.rb with any directory paths like this:

# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{config.root}/../customdir )

Note the ".." which means go above the Rails directory. This gives you a path relative to the gem.

If you prefer you can specify an absolute path, for example if you know the user will always keep his external files in "~/myfiles/". You can also choose to use ENV vars to send in whatever directory you want.

If you read about load path capabilties, look for lines that are shorthand for adding a directory to the front of the load path because you may want to put your external diretories first:

$:.unshift File.dirname(__FILE__)

Gem Build:

Edit my_gem.gemspec to add your own description, homepage, summary, etc. then build:

$ gem build my_gem.gemspec
Successfully built RubyGem
Name: my_gem
Version: 0.0.1
File: my_gem-0.0.1.gem

Now your Rails app is packaged as a gem.

The config.ru should be a typical Rails one. No special changes AFAIK.

When your user wants to install your app:

$ gem install my_gem

The gem will install in the user's typical gem directory. If you want to adjust this, see this page on rubygems: http://docs.rubygems.org/read/chapter/3

Crate:

You may also want to investigate the Crate project:

  • Crate: Packaging Standalone Ruby Applications
    http://www.slideshare.net/copiousfreetime/crate-packaging-standalone-ruby-applications

Rack:

To use config.ru here is the typical Rails setup:

# Rails.root/config.ru
require "config/environment"

use Rails::Rack::LogTailer
use ActionDispatch::Static
run ActionController::Dispatcher.new

For your project, you want to require some files before Rails. You'll want to learn about the Ruby "require" and how it finds files using LOAD_PATH.

The easy way:

# Rails.root/config.ru
require_relative 'filename'
require "config/environment"

Or to put the user's custom directory up couple directory levels:

require './../../filename'  # not the best for security

Or to use an absolute path, read about File.expand_path:

File.expand_path(__FILE__)

Or to use the current directory and put it on the load path:

$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'filename'

Lots of choices for you to consider. Hope this helps!

like image 161
joelparkerhenderson Avatar answered Oct 10 '22 05:10

joelparkerhenderson