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:
- Just creating an
app/modelsdirectory and put my model inside - 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
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 --mountablecreates 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).The other important thing you should be aware of, is that rails (actually
Bundler.require) autorequires one file for you when you add your custom gem to your gemfile: the file namedlib/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.gemspecfile as your gemfile, just add gems usingadd_dependencyinstead of the standard Gemfile syntax. If you want (and you should) learn more about ruby gems, this article is really goodThe
appdirectory is autoloaded like rails does, so addingapp/models/yourmodel.rbis 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 runyour_engine_name:install:migrationsin 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:This should be enough