How to extend Rails Engine's controllers properly? - ruby-on-rails

I am developing Rails plugin (it is 3.1 Engine) called Carrier (https://github.com/stanislaw/carrier).
In one of my rails app I want to extend Carrier's controller with some new methods - fx. add new action #comment_form to Carrier::MessagesController (I want this action only exist in my app - I don't want to add it in Engine - because it is very specific).
Two strategies I see here:
1) I copy {Carrier's plugin root}/app/controllers/carrier/messages_controller.rb file to app/controllers/carrier/ folder of my app, and then extend it (all plugin's original actions are copied to rails app controllers folder too!).
2) More accurate way I want - is just to create {My rails app}/app/controllers/carrier/messages_controller.rb and write only #comment_form method I want Carrier to be extended with.
Expecting that two controllers's content (original from plugin's folder + custom in my rails app having only new #comment_form) will superpose, I tried the second way. But Rails then stopped recognizing all original Carrier's actions (#index, #show, etc...) written in messages_controller.rb from the Carrier plugin's folder and began treating rails app's messages_controller.rb version as the only one (all original actions began treated as empty and thus began rendered through rails conventions default flow).
So my question in general is:
How to add new actions to Rails Engines controllers without copying them entirely to Rails app/controllers folder?
UPD
For now I see two solutions which allow extension of engine's controllers without serious hacks (like this gem does: https://github.com/asee/mixable_engines from this thread: Extending controllers of a Rails 3 Engine in the main app)
1) load YourEngine::Engine.config.root + 'app' + 'controllers' + 'your_controller'
inside your_controller.rb that is in #{main_app}/app/controller/your_engine folder. Notice load instead of require.
2) Devise way (according to some SO topics suggest):
In main app create new controller that subclasses engine's one + edit routes to they point to this new controller.
I am still sure some even better solutions exist. Please correct me if they do!

Your option 2) is fine because it will let you upgrade the gem seamlessly.
Your current way simply overrides the existing controller.
Say you want to extend FooController.
Create a file named foo_controller_decorator.rb in your initializer folder
In the file:
FooController.class_eval do
#your additionnal code here.
end

I know this is a very old question but in case someone else finds this question, here is a gem that does decorators nicely. It hooks into Rails ActiveSupport and adds a convention to doing decorators that is safe from circular dependencies. We have been using it in production on multiple apps for a while.
https://github.com/EPI-USE-Labs/activesupport-decorators

Related

Overriding the default views in RailsAdmin

I am using Ruby on Rails 4.2.1 with RailsAdmin. The gem works excellent, but I have the requirement that the layout of the admin panel and the forms must look different than what is generated by default. For example, the navigation should be horizontal top, the forms should order the fields in two columns.
So far I haven't find a way to copy the views locally and modify them (like in Devise for example). I have tried to replicate the views manually in the respective path under my views folder by copying the original views, but I got problems with the helper methods that are part of RailsAdmin not being accessible from my views.
I dug deeper and found that there is a task copy_views, it was referred to in questions for the older versions of the gem, but if I try to use it now rake rails_admin:copy_views, it is not available anymore.
Am I doing something wrong, or is there another way to do this?
You can create folders in your app
app/views/rails_admin/main for https://github.com/sferik/rails_admin/tree/master/app/views/rails_admin/main
app/views/layouts/rails_admin/ for
https://github.com/sferik/rails_admin/tree/master/app/views/layouts/rails_admin
Put modified files there. It can get a little messy and you will need to update the files if the gem changes.

Ruby on Rails generating controller with existing folder

I'm really new at this and I don't want to mess up my current application. I'm currently working on HTML/CSS stuff but I have a little understanding as to how to get things working.
I created a new controller by just doing
rails generate controller sign_up
I've created a index file (index.html.erb) inside sign_up folder from rails that automatically generated. Now I wanted to add more files, can I just add more files by typing in
rails generate controller sign_up send_page more_page other_pages
Will send_page, more_page, other_pages be automatically combined into the existing sign_up folder? Is this the correct way of adding pages if I want rails to add other files automatically? I just want it to ruin what I have so far so I don't want to try it for myself because I'm still really new to this,
Thanks!
To answer your question :
New files will be added to the existing folder but existing files will not be merged, you'll have to resolve the conflict by choosing between old and new files.
It seems to me you're trying to use generators for every action you need in your app :
Scaffolding is great to have a sample structure, but as soon as you start building your controller for real you need to move away from it : extend the controllers, models and views by hand. It's the only way you'll start to really understand how things are working.
If you run the generator again, it's going to complain about conflicts with existing files. Then you'll have to choose to overwrite (losing your existing changes to e.g. the controller), or not (in which case you won't get the new auto-generated methods, etc.).
You can just create a new controller action and a new view for each additional page you want to add (and a unit test, of course!). If you're using non-standard/non-restful action names, you'll also have to edit config/routes.rb to route them.
Looking at the names in your example, and your reference to "pages", I suspect what you really may want to do is create separate controllers for send_page and more_page. If that's the case, you'd run the generator separately for each new controller.

How do I use a dummy application to test a gem (preferably with Cucumber)?

I'm making a gem that will add some custom responders to an application's controllers. To test this, I'm going to need a dummy application sitting inside of a test directory that I can load and somehow generate views from.
I'd prefer to use Cucumber to test this gem, because that is what I'm most comfortable with having used it in the past.
How do I generate an application inside of my test directory like this? I will need at least a controller and a model, but views aren't necessary (the responder is only for JSON). Can I just generate a new Rails app within /features/support/dummy? How would I run the dummy application from within my test suite?
At first, do not generate anything within features/support/. That's the place for global hooks.
Your problem sounds not hard if I understood correctly. Here is my proposed steps.
Create a demo directory in project root. New demo app will be under it.
Create a ruby module/class under features/support/, with methods to generate a demo app and delete it. The demo app will be simply some directories like app/contorllers for test purpose. Making a whole new Rails app sounds overkill.
Create a tag for the Scenario which need generate app. Add Before and After hook for this tag to add and remove app for this Scenario, by calling the module/class in #2

Rails 2 route helpers in plugin

I'm trying to write a plugin, and among the tasks I want to perform I want to be able to call route helper methods from within the plugin. For instance, if I have map.resources :user, I want to be able to call user_path(:id => 1) from my plugin. I keep getting undefined method user_path error.
In rails 3, you can do this using Rails.application.routes.url_helpers, but I don't seem to be able to find an alternative for rails 2. Including ActionController::UrlWriter does not help. Any ideas?
I'm using rails 2.3.4 and i can use my regular path helpers in the controllers and views of my plugins, at least within the ones i tested.
I can't use them in the lib files for the plugins, but that's because the helpers aren't available outside the controllers (the views are dealt with inside the controllers so they can use them too). The lib files (the meat of the plugins) tends to be modules and classes which get loaded into the model environment.
Can you provide more details about what you're trying to do?
You should be able to do:
app.user_path(1)

Change Rails Scaffold Naming Scheme

I'm a happy user of RoR but have one complaint. When I do script/generate scaffold it automatically generates all my files and places them in their proper folders. However, all the different scaffolds I've created name their view files the same.
I have a bunch of index.html.erb view files and when I have them open in my text editor, it's almost impossible to tell what controller they're related to.
I'd like to change the default naming scheme of the scaffold command to name the individual files to contain their view folder name. So, instead of index.html.erb, use index.home.html.
Is there a way to do this, or am I stuck? What solutions to the multiple files with the same name problem have you Rails developers discovered?
Thanks!
You're going to be fighting the Rails' conventions by going down that path and Rails works best when you work with it rather than against it. A core part of the philosophy of Rails is that there are a set of conventions that once learned make it easy to find your way around any Rails application.
Instead of trying to redefine how Rails works, I would recommend taking advantage of the features offered by your text editor or IDE for quickly navigating to the correct view template. For example, the Rails bundle within TextMate on the Mac lets you quickly open the view file associated with a particular model and there's a plugin for Vim that provides an equivalent feature.

Resources