Rails engine gem should or should not explicitly load helpers? - ruby-on-rails

I'm wondering if a Rails engine gem, such as React on Rails, should be using this line of code to explicitly load the helper:
ActiveSupport.on_load(:action_view) do
include ReactOnRailsHelper
end
This is definitely not needed for normal Rails application use of the gem. However, some users have reported this as an issue.
Any opinions?
Here's the specific issue reported:
https://github.com/shakacode/react_on_rails/issues/385#issuecomment-209964034
This is how the react-rails gem does it (not a Rails Engine):
https://github.com/reactjs/react-rails/blob/master/lib/react/rails/railtie.rb#L42-L44

Related

How to convert existing rails application to gem

I have a rails application that is kind of similar to the active_admin gem. I want to make this existing application into a gem.
I know that you can generate a gem with:
bundler gem gem_name_here
This command creates an empty gem. Which is fine I suppose, I just don't know where to put all my code.
You're ostensibly supposed to put all of your code in the /lib directory. However, my application has an assets pipeline and an app directory with all my models, controllers, and views. I don't know where to place all of these directories in the gem. Any thoughts or ideas?
If you need me to share any code, I'll gladly share it.
You're describing a rails engine. Engine is a miniature rails application that can be used inside other rails app. For more details see official rails guide

When to load Gem code when depending on rails

I'm developing a Gem that is to be used exclusively in Rails projects. It has been developed inside of a Rails application's lib directory and is now to be extracted in a separate Gem.
Some of the classes depend on the Rails framework to be loaded though. Two examples:
class OurGem::Hookup
CONFIG_PATH = 'config/hookup.rb'.freeze
[...]
end
class OurGem::RoutingContainer
include Rails.application.routes.url_helpers
[...]
end
Normally, I load the Gem code in the Gem's main module file using require. But as this is loaded by bundler, Rails is not ready and things like Rails.application and Rails.root cannot be used. The first example could be worked around by not evaluating the path at load time, but the second one seems kind of tricky to me.
What is the proper way of doing this? Register an initializer using a railtie and require the "delicate" files there?
This is a strange setup, because your gem depends on your rails app and your rails app depends on your gem.
This looks far too coupled to me. Have you considered creating a self-contained rails engine that your main app mounts, instead?
You might be able to get away with doing this, though:
# Gemfile
gem 'our_gem', require: false
# config/initializers/our_gem.rb
require 'our_gem'
OurGem::Hookup.config_path = '...'
This ensures that your gem is only being loaded after the rails application initialises - so things like Rails.application.routes.url_helpers will be defined.

Which Prawn gems should I install for my rails app for creating a PDF?

Hi I have an app which needs to render a PDF with access to controller variables like in view, and ideally also allow me to use partials. Currently I just have installed:
gem 'prawn'
gem 'prawn-table'
These work quite well, except for the fact that they do not allow for using partials.
After some reading I found some wrapper gems such as prawn-rails, prawn_rails (!) and prawnto or prawnto_2.
Does anyone have any experience with these gems and has a strong preference for any?
My Rails version: Rails 4.2.4

Undefined method 'devise' when including User model outside Rails

I am developing an application that consists in two parts: A ruby command line application and a Rails application for the front-end.
I've been using ActiveRecord and the Rails models in the Ruby application by including them individually the following way:
Dir[File.dirname(__FILE__) + '/../../RailsApp/app/models/*.rb'].each do |file|
filename = File.basename(file, File.extname(file))
require_relative "../../RailsApp/app/models/" + filename
end
And by manually mantaining two nearly identical Gemfile files for dependencies (an approach that I think is not the best)
Yesterday I added Devise to the Rails application, and now when the ruby app tries to include the user model, the message undefined method devise for class <xxxxx> appears.
I added the devise gem in my ruby app's Gemfile, but the error continues. If I understand correctly, Devise is a Rails Engine, which is why it's not being loaded just by requiring the gem.
I read that a better approach to include Rails models inside another application is by requiring the environment.rb file, but when I tried that, I got an error with ActionMailer:
undefined method `action_mailer' for #<Rails::Application::Configuration:0xbbb54bc>
How can I solve the issue with devise? Is there a better way to include Rails models inside another application than the one I'm currently using to avoid mantaining two gemfiles ? Thanks
Even though this architecture must change in the near future, we managed to work around this error by including devise and its initializer in the ruby application.
# FIXME: Dependency needed in Rails but not in the engine.
require 'devise'
require_relative "../../RailsApp/config/initializers/devise.rb"
#Load all the models
You could try putting require 'devise' before you call the models, but I'm not sure if that would work?
I agree that this general architecture feels wrong.
I would extract the code that is common to both applications to a separate library and then include that library in both applications. Your easiest bet to do this is to make your own library into a Gem and then include it both Gemfiles. The common gems will be specified in the shared library. Making Gems is really easy. Good instructions can be found at http://asciicasts.com/episodes/245-new-gem-with-bundler (or the associated Railscast).
Your command-line app probably shouldn't need to care about Devise. If you need a User model in both, you can just include it in the shared library and then add any extra functionality (such as Devise) in the Rails app.
Does that help?
You could move the time of loading your models into the parent app initialization step, rather than at the time your gem is loading itself.
Build a shared_code gem, as your other answer suggests.
Move your shared_code loading loop into an initializer file (such as require 'user')
List the gem dependencies (like devise) of your models in the shared_code gemspec.
In your shared_code gem, include a generator to install the initializer file into apps that use the shared_code gem.
Then to use your shared code in the parent app,
Gemfile:
gem 'shared_code'
console:
rails generate shared_code:install
This is the same pattern devise and other gems use to configure themselves at the correct time in the initialization sequence of the parent application.
If you use require_relative on the devise initializer in the user.rb file itself, within the shared_code gem, you are removing the possibility that two different parent applications that are using your shared_code might need to initialize devise (or another gem that both the app and your gem depend on) differently.
Details on the generator:
You can create the shared_code:install command by creating the generator like this:
lib/generators/shared_code/install_generator.rb
module SharedCode
class InstallGenerator < Rails::Generators::Base
source_root File.expand_path('../templates', __FILE__)
def copy_initializer_file
copy_file "shared_code.rb", "config/initializers/shared_code.rb"
end
end
end
Put your model load code into lib/generators/shared_code/templates/shared_code.rb

Rails Engine Helpers not loaded

I am creating a Rails engine and I have some problem to load automaticly helpers methods from application_helper in my engine app directory. In fact for some controllers, they are loaded but for other one, they are not.
Any idea of the cause of this?
I'm not sure about Rails 3.1, but using Rails 3.0.11 I solved my problem by adding this to my Gemfile which then autoloads those helpers in the engines:
gem "rails_helpers_fix"
https://rails.lighthouseapp.com/projects/8994/tickets/1905-apphelpers-within-plugin-not-being-mixed-in#ticket-1905-22

Resources