Rails: Reference an engine from an engine - ruby-on-rails

I have a higher level app, that includes two engines:
Core (engine)
API (engine)
The API engine depends on the models present in the Core engine. I was wondering how to reference these.
What I have tried
For example, if the Core engine has the model "User", how do I reference it via the API engine?
module Api
describe User do
routes { Api::Engine.routes }
before :each do
::Core::User.create!
end
....
With this code, I received:
Failure/Error: ::Core::User.create!
NameError:
uninitialized constant Core
So I thought that I had to include the core engine in the api.gemspec file.
s.add_dependency "core", path: "core/"
however, looks like bundler did not like that.
There was a Gem::Requirement::BadRequirementError while loading
api.gemspec:
Illformed requirement [{:path=>"core/"}] from myAppPath/api/api.gemspec:21:in
I also tried
s.add_dependency "core", path: "../core/"
but that gave me a similar error.
Any idea what should be done to reference the Core models from the API engine?
Thanks!
Update
I attempted to add the Core engine into the Api engine via the Api's Gemfile. Unfortunately, I get an error. I'm starting to feel that engine's should not reference other engines. Would that be true?
/home/.rvm/gems/ruby-2.0.0-p247/gems/railties-
4.0.0/lib/rails/application/routes_reloader.rb:10:in `rescue in execute_if_updated':
Rails::Application::RoutesReloader#execute_if_updated delegated to
updater.execute_if_updated, but updater is nil: #
<Rails::Application::RoutesReloader:0x007fb53b67bce8 #paths=
["/home/myApp/api/spec/dummy/config/routes.rb", "/home/myApp/core/config/routes.rb",
"/home/myApp/api/config/routes.rb"], #route_sets=[#
<ActionDispatch::Routing::RouteSet:0x007fb53b3b8420>, #
<ActionDispatch::Routing::RouteSet:0x007fb53b6008b8>, #
<ActionDispatch::Routing::RouteSet:0x007fb53b6b9b60>]> (RuntimeError)
UPDATE on findings
In addition to the answer below, I would like to add that .gemspec files DO NOT have any information on where the gem will be stored, i.e. it will not point to a git repository, or a file path, etc. This is explained in this article:
http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/

There are 2 things to understand here:
If your Engines are supposed to be public (like Devise engine for example), then you'll want to reference your Core dependency in the gemspec. You'll not be able to reference it using path though. But for public engines, this is not a matter since public engines will be on Rubygems at some point in time.
If your Engines are private and used only to cleanup your code base, then adding gem 'core', path: '../core' to your Engine's Gemfile is fine.
The thing to remember here:
gemspec is for dependencies declaration (the gems your engine needs in order to work)
Gemfile is for local or test dependencies declaration

Related

Uninitialized constant Google::Cloud raised by Google::Cloud::Translate.new in production environment

I have a GoogleTranslate service that translates text in my app. The feature works on localhost but in production it raises
uninitialized constant Google::Cloud in my app/helpers/google_translations_helper.rb:4:in `initialize'...
Here is the Gemfile related part :
# use of google API
gem 'google-api-client', '~> 0.11', :require =>
'google/apis/analyticsreporting_v4'
gem 'omniauth-google-oauth2'
gem "google-cloud-translate"
And here is the helper/service initializer:
module GoogleTranslationsHelper
class GoogleTranslate
def initialize
#translation_service = Google::Cloud::Translate.new
end
def translate(text)
#translation_service.translate text.to_s, from: "fr", to: "en"
end
end
end
I wonder if I'm not missing something about gem version or something like that..
Does someone had the problem already?
I assume you are running Rails. Make sure you include:
require "google/cloud/translate"
somewhere reasonable, either at the top of the file that creates the client object (app/helpers/google_translations_helper.rb in your case), or in a global initialization file such as config/application.rb. (The google-cloud-translate library, like most libraries, needs you to require it before you can use it. See the snippets in the documentation for examples.)
It's not completely clear to me why this is working differently between your development and production environments, but there are usually a lot of differences in the initialization procedure between the two environments so it's not too surprising. Just make sure you're in the habit of requiring any library before using it.
As a side note, I would also recommend updating your Gemfile to call for more recent versions of the Google client libraries. Or at least make sure you've done a recent bundle update. As of this writing, google-api-client 0.11 is more than 2 years old; the newest is 0.30.8. And google-cloud-translate is at 1.3.0. It is always possible there are issues if you're on old versions.

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

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/models directory 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 --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

Use Orchestrate.io with a basic Rails Application

I have a basic application running with ruby on rails. I use sqlite3 for development and postgres for production. I recently got access to Orchestrate.io and their student tier.
I would like to know how I could possibly use this with my application. I'm not even sure this is possible. My app is really simple (add-edit-delete). But it would be interesting to have a go at using the tools.
Mostly because I cannot find an efficient and free rails db text search.
You can definitely use Orchestrate as the backend for a Rails application.
A few different options:
The Orchestrate-Ruby client has both a method client & object client. Although the object client is still considered a work-in-progress, the method client provides a solid interface for connecting to the Orchestrate API.
Use an http library (such as rest-client or faraday) to make calls to the Orchestrate API.
There's also the Orchestrate-Rails gem. Your mileage may vary with this (last updated in April).
To use Orchestrate as the only backend (no postgres/mysql), I've found the best approach is to disable ActiveRecord:
When creating a new application, you can use -O to skip ActiveRecord:
rails new my_app -O
For existing applications:
1. Remove database adapter gems from your Gemfile (pg, mysql2, sqlite3, etc.)
2. Change your config/application.rb
Remove require 'rails/all line and require frameworks you want to use, for example:
require "action_controller/railtie"
require "action_mailer/railtie"
require "sprockets/railtie"
require "rails/test_unit/railtie"
3. Delete your database.yml file, db/schema.rb and migrations (if any)
4. Delete migration check in test/test_helper.rb
5. Delete any ActiveRecord configuration from your config/environments files (this is what is causing your error)
If you run into problems/errors, stack trace should give you sufficient information on what you need to change. You might for example have some ActiveRecord configuration in your initializers.
With the Orchestrate-Ruby client, you can define a model like so:
class Foo < Orchestrate::Collection
...
end
I'm still undecided on what the best approach for defining the Application object is, but one way I've done it is by making a global object with an initializer:
# /config/initializers/orchestrate.rb
$App = Orchestrate::Application.new(ENV['ORCHESTRATE_KEY']) # using dotenv gem
$App = Orchestrate::Application.new('your_key_here') # without dotenv
From here, you can then access the $App object in your controllers (to initialize the collection object):
class FoosController < ApplicationController
def index
#foos = Foo.new($App, "foos").each
end
end
Leveraging model concerns might be a better way to share the Application object/API key between models. Or you could roll your own classes on top of the method client.
Hope this helps!
I'll be looking into how to accomplish this in a more 'Railsy' way.
EDIT: also, textacular is a ruby gem which provides full-text search and indexing for postgres

Does gem spec. name have to equal the gem constant name?

tl;dr
I'm trying to break out a part of a larger application as a mountable engine. The engine will exist in different flavors, each contained in their own gem. I can't get the gem name be different from the engine constant name.
Details
The extracted part contains logic for registration, authentication and session handling. The application is used by clients in different parts of the world with different requirements and regulations regarding the end customers using the product. This prompted us to create separate modules for these needs for each regulatory region. They currently live in the lib directory and the different implementations are loaded depending on config.
The goal with the engines is that you mount the appropriate engine and the routes file routes all the concerned calls to the engine.
Since we have several such registration modules, and more to come, we need to maintain several gems for the variants. I'm trying to make it so that the gems have different names (auth_A, auth_B, etc) but the contained engine has the same contant name, Auth::Engine.
That way we can include the correct gem in the Gemfile and the rest will just work since the endpoint it should rout to is always the same regardless of what version is running.
Problem
The problem I run in to is that I can't seam to get the gem to have one name and the engine constant another...
If I generate a new engine names auth it works fine to mount it in the main app. If I then change the gem name and containing folder to auth_a and update the host apps Gemfile it stops working, I can bundle fine but when I try to start the host app it fails when it ties to mount the engine, complaining that Auth::Engine is an undefined constant.
My, slightly redacted, gemspec looks like this:
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "auth/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "auth_a"
s.version = Auth::VERSION
s.authors = ["Jonas Schubert Erlandsson"]
s.email = ["jonas.schubert.erlandsson#xxxxxx.com"]
s.homepage = "http://some.page.on/the/internet.html"
s.summary = "Authentication module for A"
s.description = "This engine, when mounted in Host app, handles registration, account update, login and logout of users."
s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails", "~> 3.2.13"
end
The only thing I have changed from the generated scaffold is s.name = "auth_a". The relevant line from the host apps Gemfile: gem 'auth_a', path: "../auth_a"...
I have looked through the entire source tree trying to find where it infers a name from the gem name but I can't see it. I don't know what I'm missing and the gem spec docs weren't much help for this either ... I didn't think that the gem name was bound to the constant names of the gem, but maybe I'm wrong? Can you override it? Or am I missing something else?
The answer to this was to simply add this to the line in the host apps Gemfile: gem 'auth_a', path: "../auth_a", require: 'auth'.
So it looks like it defaults to requiring, that is auto loading, a constant based on the gem name, but the require call tells it what to require instead.
Hope it helps someone else as well :)

Converting Rails 2 plugin to Rails 3 gem

So there's this great plugin I've gotten used to using in my Rails 2 projects called Bootstrapper. It essentially duplicates the functionality of the seeds.rb file, but I like it because it lets you break up your bootstrap process into concise chunks.
Anyway, I've gone so far as to fork the project and attempt to turn it into a Rails 3 gem. I've been able to get the gem to initialize and register the rake tasks and generators OK. However, I'm running into a problem with the Bootstrapper class itself. It won't load in the Rails project unless it's in a module.
That is, if I place the Bootstrapper class in a file by itself and require that file in my Railtie, then in my Rails app, it can't find the Bootstrapper class. If I put the class in a module and call Bootstrapper::Bootstrapper everything is peachy.
The code that actually requires the Bootstrapper class is this:
ActiveSupport.on_load :active_record do
require 'bootstrapper/bootstrapper'
end
The source is available here:
http://github.com/jrmehle/bootstrapper/tree/make_gem
Autoload paths actually has an annoying feature of following filesystem paths. For example in your lib or extras (depending on what you autoload) you might have the following file structure:
lib/bootstrapper/bootstrapper.rb
# in this case, Bootstrapper::Bootstrapper.class = Class in rails c
# ie: you don't get a NameError exception
More specifically,
lib/bootstrappers/bootstrapper.rb
# Bootstrapper::Bootstrapper => NameError
# Bootstrappers::Bootstrapper => works
If you really want the other way, you can move everything into your lib/bootstrapper.rb source file but meh, I don't like doing that, that's not how gems are organized. In rails3, you'll find the autoloading pretty nice once you use modules everywhere (which can be painful).
Rails3 uses /extras instead of /lib but it's not required, it's just the default (commented out) from rails new. To switch, you just autoload extras instead of lib.

Resources