Include views in a Rails plugin - ruby-on-rails

I'm building a Rails plugin that currently provides controllers and models to an app. However I get a missing template error when it comes to views. I have the following:
%w{ models controllers views }.each do |dir|
path = File.join(File.dirname(__FILE__), 'app', dir)
$LOAD_PATH << path
ActiveSupport::Dependencies.autoload_paths << path
ActiveSupport::Dependencies.autoload_once_paths.delete(path)
end
The controllers and models are loaded but not views. The Rails guide says it can be done but doesn't have an example. Is there a way to include them (or a similar alternative)?

You didn't include the version you're working with ... reason that's important is after rails 4 came out - you're not supposed to be doing plugins anymore ... Gem Vs Plugin Vs Engine in Ruby on Rails.
If you're not supporting a legacy app, would highly recommend going with a 'rails engine', the Devise Gem is one great example...it's a self-encapsulated app, with it's own view files & sounds exactly like what you're trying to do. Additionally, it shows you can even use generators to move your default views/controllers out of the engine & change the basic routing if someone needs to customize your work further.

Related

Rails Spree internals

I am trying to use Spree with my RoR application. Ok, I do follow all those guides and FAQs on official website when I want to customize something. That's ok and no problem with it. One question, to which I could not find a clue -- how is that possible, that there is nothing in apps/view, apps/models folders, but it's still working? I mean, yes, I can create something in these folders and redefine the behavior of my views (actually, this is one of the ways of customization), but I really want to understand the internals. I am pretty new to Rails and got used to classic app folder structure.
what you are wondering about is the magic of Rails Engines.
Ruby on Rails allows you to define Engines (your app is one too) and when it looks for views/controllers/etc.. all mounted engines are part of the search path.
So the view is inside the Spree gem, not visible to you - but it still looks in there.
If you put something in your view folder with the same name, it will take precedence over the views in the Rails engine you have in the Gem.
Here is a good guide on how Engines work in Rails:
http://edgeguides.rubyonrails.org/engines.html
One good example of these Engines is the jQuery-rails Gem you probably use inside your Application.
It has no code at all (except for some fallbacks for Rails 3.0 and below that don't have an asset pipeline), but the jQuery.js file in the app/assets/javascripts folder. And since the engine is in the load path you can require the asset that's in there..
The engine itself has the same folder structure as your app (app/views, app/controllers ...)
You can look at the internal structure of Spree here: https://github.com/spree/spree/tree/master/core/app

Creating gems with app assets

I followed http://railscasts.com/episodes/245-new-gem-with-bundler to make a gem with bundler and this is great for gems where i only need a lib, is there a standard practice for gems where i need to create mini apps with assets/controllers/models/views ?
You would be looking to create an engine at that point. Reading the Engines Guides guide should give you a great start on that.
The bare-bones components you need inside your gem are a file at lib/your_gem.rb that serves the purpose of simply requiring whatever it is your gem needs. If your gem has no other dependencies, then it looks like this:
require 'your_gem/engine'
One line, so much power. The lib/your_gem/engine.rb file it requires has this code in it:
module YourGem
class Engine < Rails::Engine
end
end
By simply inheriting from Rails::Engine, this triggers an inheritance hook on Rails::Engine that notifies the framework that there is an engine at the location of your gem.
If you then create a file at app/assets/stylesheets/your_gem/beauty.css, you can then include that in your application (assuming you have the asset pipeline enabled, of course) using this line:
<%= stylesheet_link_tag "your_gem/beauty" %>
Now that I've given you the short version of it, I really, really, really recommend reading the Engines Guide top to bottom to better understand it.

How to extend Rails Engine's controllers properly?

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

Loading parts of a Rails 3 application

I am developing a gem for Rails 3 that consists of two main components. The first is a rails generator that adds some new files/folders to a rails project. The second is a runtime environment that loads all the aforementioned files (some ruby classes that use my DSL) as well as a portion of the default Rails stack. Essentially it's everything you'd expect to be able to access in rails c, sans routing, controllers, helpers and views. What is the proper way to load a Rails environment, except for specific portions?
Sidenote: I'd love to see any good articles regarding requiring Rails applications.
I am not entirely clear what you mean, or if this will help, but it sounds similar to something I do in a utility I wrote.
My utility loads the environment like so:
#!/usr/bin/env ruby
require File.expand_path('../../config/environment', __FILE__)
The require of the ../../config/boot will cause the gems defined in your Gemfile to load. So if you needed only part of the Rails stack then you would only require that part of the stack in your Gemfile.
This gives me my rails context, access to models and other resources.
(UPDATE)
To skip parts of the rails stack - take a look at how its been done to swap out ActiveRecord:
http://www.mongodb.org/display/DOCS/Rails+3+-+Getting+Started
Hope that helps.
Maybe you need Rails::Initializable?
You can do like that:
initializer "active_support.initialize_whiny_nils" do |app|
require 'active_support/whiny_nil' if app.config.whiny_nils
end

Where should I place my own "module" within rails application?

Some functionality within my rails application looks better as if it was a separate "module", that should be accessed via require. For example, assume it's a function to calculate Fibonacci numbers.
The functionality is independent on rails application and can be reused in other projects, so it should not be stored near application controllers and models, I suppose. But since I'm not going to detach it into separate project, thus placing it to vendor folder seems like not the right thing.
Where should I place it then?
Rails < 5
Before Rails 5, the place to put reusable code such as this is in the lib directory. However you do not need to require anything as lib is already in the load path and it's contents will be loaded during initialization.
If you need to extend an existing class, you define your module first and then include it by sending it as a message to the class you wish to extend, e.g.
module MyExtensions
def self.included base
base.instance_eval do
def my_new_method
…
end
end
end
end
ActiveRecord::Base.send :include, MyExtensions
Rails 5+
The answer is for Rails 5 onwards. TLDR: app/lib is the convention now.
Note all the above answers were written before Rails 5.
\lib
Rails 5 discourages you from using \lib. While you are discouraged from using \lib you still can as long as you add \lib to eagerloading.
# config/application.rb
config.eager_load_paths << Rails.root.join('lib')
\app\lib
Nesting lib off app is now a common convention because any directory off app is automatically eagerloaded.
Reference
Rails guide - autoloading and eagerloading
Stackoverflow - confusing about autoload_paths vs eager_load_paths in rails 4
Stackoverflow - Why use app-lib instead of lib in rails
There is a lib directory in RoR projects which fits well for that purpose - I place common bits of code in form of "libraries" there. Anything from extending ActiveRecord classes to reusable utility methods.
I'll often put stuff in lib, it turns out that anything under lib is in the load path and doesn't need to be required at all.
edit: After Steve's comment, removed the bit about having to require the files. Also, removed a couple requires from some of my code :P

Resources