How to auto-reload changes in a Rails Engine? - ruby-on-rails-4

I have a Rails 4.1.0 mountable engine. In the engine's application_helper.rb:
module MyEngine
module ApplicationHelper
def test123
"test123"
end
end
end
The method is in the dummy app's view general/index.html.erb view:
%<= test123 %>
This works. However, when I change the string returned by def test123 and refresh the browser, the new string is not displayed.
Of course, restarting the web server in the dummy app shows the new string.
So the question is, how to reload the engine's files without having to restart the web server?
PS. I am preferably looking for a way to do this using Rails itself, or a specific gem that solves this problem (but not the generic gems like Guard, Spork etc. although if all else fails, I will consider those too.)
PPS. There are similar questions on SO, but I have tried them all (even though they are for Rails 2.x, 3.x), and they have not worked for me.

You should explicitly require dependent helpers:
# engines/my_engine/app/controllers/my_engine/application_controller.rb
require_dependency "my_engine/application_helper" # This is a key point!
module MyEngine
class ApplicationController < ::ApplicationController
helper ApplicationHelper
...

you can use some thing like zeus which is helping a lot in watching project files for changes except in some cases when you change the configuration files it doesn't work and needs to be manually restarted.
but overall in most cases this works more than awesome

Related

Why does a Rails engine's gem name have to "match" the module/namespace name?

I am building my first Rails engine. I called it my_engine, so it generates the files
lib/my_engine.rb
lib/my_engine/engine.rb
lib/my_engine/version.rb
That all have the module named MyEngine. And in the gemspec, the gem name is also set to my_engine.
If I create a model my_model, it goes into app/models/my_engine/my_model.rb, and is generated as
module MyEngine
class MyModel < ActiveRecord::Base
end
end
If I make a class method in here, and put the gem in a Rails project, it all works fine.
def self.hello
"Hello from your Engine's model!"
end
$ bundle exec rails c
[1] (pry) main: 0> MyEngine::MyModel.hello
=> "Hello from your Engine's model!"
However, I do not want the gem name to be my_engine. But if I change the name in the gemspec to something else, like what-i-really-want-to-name-it, everything stops working. Rails cannot see my model, although it can see my namespace. I am indeed changing the gem name in the Rails app and re-bundling, so it's not an issue of that.
$ bundle exec rails c
[1] (pry) main: 0> MyEngine::VERSION
=> "0.0.1" # default version from engine generation
[2] (pry) main: 0> MyEngine::MyModel.hello
NameError: uninitialized constant MyEngine::MyModel
from (pry):2:in `__pry__'
Why is "this" tied directly to the gem name? Is there any kind of workaround for this? I would really like to have the gem name and the module name to be different values.
Using: Rails 4.2.6, Ruby 2.3.0
The answer is that one of Rails' foundational concepts is:
Convention over configuration.
When you decide to override the principle of Convention with Configuration, that's a Rails anti-pattern. Is it possible to to do it and be happy and have a working Rails app? Sure, but it's not code you would want to show as an example of your work on a Rails app.
So, the convention is that the module name matches the gem name. It's just a convention, but since convention is the mutually agreed law in Rails town, you're going anti-pattern when you don't follow it.
Added in response to OP comment
Rails engines are typified by the fact they use an isolated namespace and isolated resources. Gems don't, so in effect, the answer is yes, using a Rails engine does enforce a namespace convention that does not exist for a gem. And that namespacing is used by the middleware to keep the main_app separated from the engine at runtime. Two examples to illustrate:
An extreme example: you can have an app mounted as as engine on itself. Namespacing isolates one from the other so the middleware services act on the right processes which are only differentiated by the namespace.
Another example: 2 engines mounted on a main_app. Now there are essentially 3 apps running. How would you want to implement non-conventional namespace isolation in this case?
So...
It is possible to hammer in a nonconforming namespace in a Rails engine? Probably. I've never tried. But your engine will not be portable. And worse, someone installing it will not be able to mount another engine that is conforming (and sharing the main_app and the middleware stack) because you've forced them into a configuration maze that breaks a conventional Rails engine. This last part is a theory.

Undefined method error for helper method on view in rails Engine

I am facing the following problem: in my application I use engines. Let's say I have a shop engine. Within that shop engine I have two controllers: carts_controller and products_controller and their helpers: carts_helper and products_helper.
Now on my views/shop/products/index.html.erb view, I try to call the method cart_action which is defined in helpers/shop/carts_helper.rb. However, unfortunately I get a undefined method `cart_action' for #<#<Class:0x007fb3627af090>:0x007fb3627aab08> when I do this. When I place the same method in helpers/shop/products_helper.rb I do not get this message and the method works fine.... Why can't I use the method from carts_helper, but can I use the method from products_helper? In a normal rails app I can use any helper method on any view, right?
It might have to do something with the namespacing i.e. the helper files are not in helpers but in helpers/shop however this helps prevent conflicts with helpers from other engines or apps...
module Shop
module CartsHelper
def cart_action(package_id)
#some code
end
end
end
How I call it on shop/products/index.html.erb:
<%= cart_action(package['id']) %>
Could it have to do with the fact that I inherit functionality for my applications_controller from my main app?:
class Shop::ApplicationController < ApplicationController
end
instead of
module Shop
class ApplicationController < ActionController::Base
end
end
FWIW my routes for this engine looks like:
Shop::Engine.routes.draw do
resources :products, only: [:index]
# shopping cart
resource :cart, only: [:show] do
put 'add/:package_id', to: 'carts#add', as: :add_to
put 'remove/:package_id', to: 'carts#remove', as: :remove_from
end
end
Thanks for any help in advance!
Note: I do not want to use my helper method in my main app, rather just on another view in the same engine.
In addition to the ApplicationHelper, only view-specific helpers are accessible to the view. Since this is your product-related view, only the ApplicationHelper + ProductsHelper are accessible. So the solution is to either move this method to ProductsHelper or to ApplicationHelper.
I found two ways to make the methods available to other views as well:
By creating an initializer in my /my_engine/lib/my_engine/engine.rb file as described here: https://stackoverflow.com/a/9641149/3519981
Or by including helper :all in my controllers/myengine/application_controller.rb file as described here: https://stackoverflow.com/a/1179900/3519981
Note both will make all the helpers available in the main application as well. For me that is not a problem (at the moment).
Not sure if anyone here has the same issue as me, but basically after I made a new custom helper and it wasn't accessible in a view, all I had to do was restart the rails server. I felt pretty silly after realizing that, well also relieved :-)

How to use Rails 4.1 to preview e-mails defined inside a mountable engine

We have SomeMailer set up inside our engine. Upon generation of the mailer, Rails creates an SomeMailerPreview class, with the comment:
# Preview this email at http://localhost:3000/rails/mailers/some_mailer/test
However, once I run the Dummy app inside my engine, that URL doesn't resolve.
The engine is mounted on the root path '/':
mount MyEngine::Engine => "/"
I've tried different combinations of the url with the engine name in there, but doesn't resolve.
Is it possible to use the preview feature for a mailer inside an engine?
A little late on this but figured I would answer anyway. You can get your previews recognized by letting rails know where the previews path is. By default, it looks in
"#{Rails.root}/test/mailers"
and so your mailer previews have to be there for the url to resolve correctly. But you can change this by setting the path yourself in the development.rb file of Dummy
config.action_mailer.preview_path = "#{YourEngineRoot}/test/mailers"
And placing your previews in the path given. Your Url should resolve correctly after that.
I have the same issue. Luckily in my case, my engine is directly reliant on the main application. In the main application, I have my mailer previews even though the mailer is within my engine.
class ApplicantMailerPreview < ActionMailer::Preview
# Accessible from http://localhost:3000/rails/mailers/applicant_mailer/applicant_email
def applicant_email
recipient = MyEngine::ApplicantEmail.all.first
applicant = recipient.applicant
job = applicant.job
MyEngine::ApplicantMailer.applicant_email(job.id, applicant.id, recipient.id)
end
end

Rails console: Unable to autoload constant

I have a Customer_ratings model that allows users to leave feedback on each other. The web app is working properly, and feedback is collected, stored and displayed.
I wanted to go in and delete some feedback through the rails console, but when I enter Customer_rating.all, I get the following error:
LoadError: Unable to autoload constant Customer_rating, expected /Users/myapps/app/models/customer_rating.rb to define it
Similarly, if I enter Customer_rating[0], I get:
RuntimeError: Circular dependency detected while autoloading constant Customer_rating
I don't have this issue while accessing other tables through my console.
What could be causing the issue, and why wouldn't this error prohibit Customer_ratings from working properly through the web app?
It seems like a case of messed up naming convention.
As per Rails naming convention, file names should be in snake_case and class names in CamelCase. In your scenario, the file name should be customer_rating.rb and class name should be CustomerRating.
After making these changes, use CustomerRating.all(as the updated class name is CustomerRating) to fetch all the customer ratings. Do not use Customer_rating.all.
I'd also like to add a scenario of this problem that I found for future reference.
I'm running Rails 4.0 and I had this same problem but what happened was I had a model named Student inside student.rb that was contained in a folder called Student. I didn't realize it at first but the folder name was the problem. Changing the folder name to something other than a model name solved the problem.
If the naming convention is not off, like in this question, it may be an issue on initial first load if you're making a lot of requests at the same time. I experienced this with nested controllers Api::LocationsController.
I solved it by enabled eager_load in development env:
Rails.application.configure do
...
# Enabled this to avoid crash unable to autoload controller
# Error happens when you start and stop server on initial requests
# solution found via https://github.com/rails/rails/issues/32082#issuecomment-367715194
config.eager_load = true
I based this off of rails issues comments: https://github.com/rails/rails/issues/32082#issuecomment-367715194
You just need to modify the name of the Module
For example if the link is http://sairam.esy.es/users/customer_rating then
you controller should like be
module Users
class RatingController
# ...
def customer_rating
# ...
end
# ...
end
end

Helpers in Rails 4

I make an application writed in Rails, it's growing fast and I learning with it. But I'm don't understand about helpers.
application_helper.rb
module ApplicationHelper
# This file it's empty
end
users_helper.rb
module UsersHelper
def avatar
# Do something
end
end
customer_helper.rb
module CustomerHelper
# This file it's empty
end
Why in any customer's view can call avatar helper method on user helper module?
Then, why separate helpers in many files?
Thanks in advance.
P.S: Rails' version 4.
Because all helpers are included in all controllers, by default. The separate files are really just for logical separation in this scenario. You can change that behaviour though:
By default, each controller will include all helpers.
In previous versions of Rails the controller will include a helper
whose name matches that of the controller, e.g., MyController will
automatically include MyHelper. To return old behavior set
config.action_controller.include_all_helpers to false.
http://api.rubyonrails.org/classes/ActionController/Helpers.html
To add to Mike Campbell's answer:
Framework
As magic as it is, Rails is a set of files which are called
sequentially
These files hold classes, methods, etc; but they're still files.
And that means that when you run an action through Rails, it loads up
a series of other dependent files to help it run (that's what a
framework is)
The design of Rails is such that your helper methods are all
loaded each time you run an action. I don't know why, but it helps
administer the methods for different areas of your app
So to answer your question, there's no real reason why the helpers are split up, at least with Rails 4