views:

50

answers:

2

Hello!

I'm building a simple CMS on Rails and I'm thinking about how I can package it so that I can easily reuse it in other projects and eventually share it as open source.

This leaves me wondering if it's possible to package it as a gem, with views, controllers, models, images, stylesheets, etc, and later just include it in a new site simply by adding the gem to the Gemfile?

I have no idea if this is possible or not but maybe someone here could help me find out?

+3  A: 

Package it as an engine gem.

See: http://www.themodestrubyist.com/2010/03/05/rails-3-plugins---part-2---writing-an-engine/

Jamie Wong
Interesting! I'll take a look at it. Cheers!
Erik
+1  A: 

Engines are awesome!! Definitely look at the link Jamie provided in his answer..it is a good starting point. Engines are the way to go with Rails 3. I feel that it is one of the most powerful features of the framework, and I converted almost every piece of common functionality in my apps into engines. IT saves you a ton of time if you are creating alot of apps. Here is my current workflow when creating engines:

Step 1: gem install jeweler if you dont have it. Then create a blank gem using jeweler.

Step 2: update the Rakefile provided by jeweler with your gem info and any dependencies. You may need to add a filelist so the gemspec can point to the correct files, and exlude any files you dont want when you build it.

gem.files = FileList['lib/**/*.rb','[A-Z]*', 'lib/**/**/*'].to_a

Step 3: Add your Rails application structure - app/controllers, app/views, etc.. to the top-level directory in the gem. You can also include /config directory for your routes.rb which will be appended to your main apps routes.

Step 4: Set up your lib directory like this:

/lib/your_engine_name.rb (require engine.rb in this file and any other files in lib that you need) /lib/your_engine_name/
/lib/your_engine_name/engine.rb

Step 5: Add code to engine.rb:

 require 'your_engine_name'
 require 'rails'

 module YourEngineName    
  class Engine < Rails::Engine  
    #load rake tasks go here  
    #initializers go here     
  end
 end

Step 6: Add all your custom engine code to app/* and lib/your_engine_name/

Step 7: Build the gem using "rake build". You must do this so Bundler can see your files in the gemspec (which is generated when you build) in step #8

Step 8: For local testing reference the local path in the Gemfile of your main application where you want to include the engine:

#Gemfile  
gem "your_engine_name", :require => "your_engine_name", :path => "/your_engines/your_engine_name"

Step 9: bundle install and start up your local app server.

Step 10: Since you are referencing the local path, you can make changes to the gem and see them in your main app without rebuilding (unless you need to add new files to the gemspec)

Step 11: put your gem source on github so you can use it in prod. Make use of jewelers versioning rake tasks. I combine some of the rake tasks like this in order to do the git push and tag in the same step and include them in the engine's Rakefile:

namespace :version do  
  desc "create a new version, create tag and push to github"  
  task :github_and_tag do  
    Rake::Task['github:release'].invoke  
    Rake::Task['git:release'].invoke  
  end  

  desc "bump patch push to github"  
  task :patch_release do  
    Rake::Task['version:bump:patch'].invoke  
    Rake::Task['version:github_and_tag'].invoke  
  end  

  desc "bump minor push to github"  
  task :minor_release do  
    Rake::Task['version:bump:minor'].invoke  
    Rake::Task['version:github_and_tag'].invoke  
  end  

  desc "bump major push to github"  
  task :major_release do  
    Rake::Task['version:bump:major'].invoke  
    Rake::Task['version:github_and_tag'].invoke  
  end  
end  

Step 12: When deploying to production, change the source in the Gemfile to github and reference a version tag:

gem 'your_engine_name', :git => '[email protected]:yourusername/your_engine_name.git', :tag=>"v1.1.1"

I am using this hack to reference 2 gem sources in Gemfile until they add that feature to Bundler: http://www.cowboycoded.com/2010/08/10/using-2-sources-for-a-gem-in-different-environments-with-bundler/

Let me know if you have any problems with this. I am working on a blog post to show this in more detail and I may have left out a few things.. just writing this off the top of my head right now..

cowboycoded
Thanks a LOT for this answer and the time you put into it! Awesome!
Erik