Sharing a mongoid model between 2 applications - using engine vs. plugin

268 Views Asked by At

I want to share a model between 2 (maybe more in the future) of my rails apps. I couldn't find any clear suggestions, but I picked up some of the questions and answers I've read and came to a conclusion that it has to be done with a "gemmed" plugin engine. I decide to go with an plugin, because I read that engine is simply a kind of a "full" plugin.

So I created a plugin using: rails plugin new my_models --skip-active-record --skip-test-unit --dummy-path=spec/dummy (the options are for skipping activerecord as an ORM and using rspec for testing).

After I created the plugin, I got the following files:

my_models.gemspec  Gemfile  Gemfile.lock  lib  MIT-LICENSE  Rakefile  README.rdoc  spec

I tried to include the model using the following methods:

  1. Just creating an app/models directory and put my model inside
  2. As suggested in this tutorial (and I could see in devise's github), I created a generator in an attempt to generate the model.

Both of them failed, and then I decided to go with the engine suggestion (by just adding --mountable to the options list of the "rails new" command), I got the full rails app structure (with app, bin, db and the rest of the directories), put my model in the app/models dir and it worked like a magic!

As I believe I'm a programmer and not I magician, I don't to do such magics, so can you tell me what's wrong with both of my thin plugin solutions (using generator/creating a model)?? Moreover, what are the advantages of using those generators?

I'm attaching my generator's code, maybe I miss something:

require 'rails/generators/named_base'
require 'mongoid'

module Mongoid
  module AttackGenerator
    def generate_model
      invoke "mongoid:model", [name] unless model_exists? && behavior == :invoke
    end

    def inject_field_types
      inject_into_file model_path, migration_data, after: "include Mongoid::Document\n" if model_exists?
    end

    def migration_data
      field :link_url, type: String
      field :token, type: String
    end

    def model_exists?
      File.exists?(File.join(destination_root, model_path))
    end

    def model_path
      @model_path ||= File.join("app", "models", "#{file_path}.rb")
    end
  end
end
1

There are 1 best solutions below

0
Francesco Belladonna On

An engine (very good guide) is basically a small Rails app: has controllers, can inject into your rails code in various ways (sharing classes/controllers and such) and most important, can use your main Rails application code transparently.

After thinking a bit about it, I believe you need an engine, the reason is, a simple model still require migrations. Notice that an engine is basically a gem plus some few additions provided by rails.

Although miguiding (plugins in rails are not used anymore), the command rails plugin new blorgh --mountable creates a gem which is a rails engine.

How do you understand if a gem is a rails engine? By the file engine.rb (can be named differently, the contents are the important stuff).

module Blorgh
  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
  end
end

The other important thing you should be aware of, is that rails (actually Bundler.require) auto requires one file for you when you add your custom gem to your gemfile: the file named lib/yourgemname.rb, in this case, lib/blorgh.rb. That's your entry point. Aside from that, all the other things (gemspec and all the other files) are things created for rubygems. The important part is that you use .gemspec file as your gemfile, just add gems using add_dependency instead of the standard Gemfile syntax. If you want (and you should) learn more about ruby gems, this article is really good

The app directory is autoloaded like rails does, so adding app/models/yourmodel.rb is a good way to have your model autoloaded and shared with all your apps. The last important thing, are migrations. You have to remember to run your_engine_name:install:migrations in your Rails app to copy your migrations from your engine to your rails app and run them (otherwise you can see the suggestions on this article)

And you are ready. One note! To install your gem you have two options: use your remote git repository (best option) or for local development you can use :path, here two examples that can be added to your Rails apps gemfiles:

# Use this only for development purposes
gem 'yourgem', '1.0.0', path: 'your/local/path/to/gem'

# Use this for deploy and such, you'll need access to that repository
gem 'yourgem', '1.0.0', git: 'yourgiturl'

This should be enough