简体   繁体   中英

Rails Engine: Create dummy model for relations?

I am trying to make a Rails Engine that can be plugged into my applications and manage friendships between users. To do that, all the logic dealing with friend requests, acceptance, etc, is going to live in a Rails Engine.

When I create my Friendship model, it needs a belongs_to relation for two Users (two friends). But, I don't want to tie a user to this engine. I want this engine to work generically with any User an application has established.

What technique does one use to create a dummy User that is never to be included in the host application? (I want to avoid a migration of the engine pulling in this dummy User .)

Update: I removed the second question, pertaining to how to then override the engine's User with the host app's User . I found the answer to that in the guides ( http://edgeguides.rubyonrails.org/engines.html#configuring-an-engine ).

tl;dr

  1. cd into engine's dummy app root
  2. run rails g model User
  3. run rake db:migrate

Discussion

I came here looking for an answer, but seeing how there were no answers, I will share how I ended up solving this.

When creating a mountable Rails engine (via something like rails plugin new . --mountable --dummy-path=spec/dummy ), Rails will generate a "dummy" app for your engine that will serve as the main app an engine would normally be required into.

Us RSpec users use the "--dummy-path" directive and put the dummy app in /spec folder, but for you it may be elsewhere. Find where it is and cd into that dummy app's root.

Once inside the dummy app, call rails g model User to generate a User model for the dummy app. Run rake db:migrate to add the table.

Now you have a placeholder User model that should function acceptably in tests as an association.
You may want to define some mock methods on User , do so in the model definition file located in /path/to/dummy/app/models/user.rb

Naturally, you can make a full-fledged model with associations and logic instead of just a placeholder right there in dummy.

Migrations and code in dummy app are not used when an engine is included in an app,as rake railties:install:migrations will show.

This might be a bit hacky!

I have an app with several engines. Each engine needs to call a limited amount of functionality in the other engines.

For example, Engine1 and Engine2 don't depend on (require or load) each other. However, a model such as Engine1::User might need to call a selection of records using a scope called all_active from the Engine2::Department model. That's okay inside the context of the overall Rails app into which both engines are loaded as Gems.

The problem arises for the developer of Engine1 when they want Engine2::Department.all_active , and they don't have it.

The solution I am using is to create a "mocked model" (for want of a better phrase) in Engine2's test/dummy/app/models (or spec/dummy/app/models if you're using RSpec) folder.

Something like this will work in Engine1:

# spec/dummy/app/models/engine2/department.rb
require '../../app/models/engine2/department' if File.exist?('../../app/models/engine2/department')

module Engine2
  class Department
    unless Engine2::Department < ActiveRecord::Base
      def self.all_active
        [{ id: 1, name: 'Finance', active: true},
         { id: 2, name: 'Sales', active: true}]
      end
    end
  end
end

Because this file is in the dummy app, it won't get found by the main Rails app, so it won't interfere there. It will only get loaded in the dummy rails app during development and testing.

FYI, the unless Engine2::Department < AR::Base detects if the Department model inherits from ActiveRecord already, and if it does, the enclosing code won't be loaded. This is a safety mechanism to prevent the class method all_active from being overwritten by the hard-coded sample data in the hash shown above.

If you have a lot of engines in your system, perhaps you should consider creating a git repo of an engine template that can be pulled into each engine (containing common code, etc).

Your sample data might benefit from being OpenStruct objects, so your hash keys can be accessed using dot notation. You could then write code such as @engine2_departments.first.name rather than @engine2_departments.first[:name] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM