How do I reference Rails in a gem I'm making? - ruby-on-rails

I'm making a gem for Rails. I need access to the ApplicationController because I'll toy with it. Absolutely nothing online gives information about what to do with gemspec and then somehow manage to get Rails accessible in my gem.
I imagine the goal is eventually to be able to talk to Rails like:
module Rails
module ActionController
#code
end
end

If you are developing a gem exclusively for Rails I strongly recommend you generate the initial scaffold using rails plugin new gem_name. There's a ton of info on developing rails plugins.
The initial structure generated looks like this:
gem_name
gem_name.gemspec
lib/
gem_name.rb
gem_name/
version.rb
engine.rb # if generated using --mountable
The whole rails environment becomes available [edit: after your gem is loaded] so extending ApplicationController can be done like this:
# lib/gem_name.rb
require 'gem_name/controller_extensions'
module GemName
end
# lib/gem_name/controller_extensions.rb
module GemName::ControllerExtensions
# bleh
end
# dummy_application/app/application_controller.rb
class ApplicationController < ActionController::Base
include GemName::ControllerExtensions
end
Look at this question.

Related

How can I extend my controller from installed Spree gem's controller?

I have spree gem installed successfully. I don't need spree_frontend. Here is the Gemfile
gem 'spree_core', '4.2.0.rc2'
gem 'spree_backend', '4.2.0.rc2'
gem 'spree_sample', '4.2.0.rc2'
gem 'spree_cmd', '4.2.0.rc2'
gem 'spree_auth_devise', '~> 4.2'
So I want to extend my ApplicationController from Spree's BaseController. Here is the code:
class ApplicationController < Spree::BaseController
include Spree::Core::ControllerHelpers::Order
end
But I get following errors:
uninitialized constant Spree::BaseController (NameError)
How can I extend my controller from installed Spree gem's controller?
The problem you're running into is that Spree::BaseController already inherits from ApplicationController; see https://github.com/spree/spree/blob/master/core/app/controllers/spree/base_controller.rb. This is to allow your ApplicationController to define things like current_user and similar basic functions before Spree sees it.
Declaring them the other way around as well creates a circular dependency, and the class loading fails as a result. Without changing Spree itself, the only fix is to do something else.
Instead, to have your controllers use Spree::BaseController as a superclass, first define ApplicationController in the more usual fashion e.g.:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# ...
end
then invent a new abstract controller, for your own use, that inherits from Spree, e.g. let's name it StoreBaseController:
# app/controllers/store_base_controller.rb
class StoreBaseController < Spree::BaseController
include Spree::Core::ControllerHelpers::Order
# ...
end
This StoreBaseController can now be used in place of ApplicationController when defining more specific controllers. It works because it doesn't create a loop in the inheritance tree, which now looks like this:
Note: if you're also using the rails generator command to produce controllers or scaffolds from templates, be aware that the generator has ApplicationController hard-coded in the templates, so you'll need to amend them once created.
Is there any reason why you need to extend strictly ApplicationController?
I advise you alternative approach to create a new Base controller class, and then inherit all the children from it and leave ApplicationController to basic rails
app/controller/my_base_controller.rb
class MyBaseController < Spree::BaseController
def foo
# ...
end
end
app/controller/my_resources_controller.rb
class MyResourcesController < MyBaseController
def bar
# ...
end
end
As the errors states, Spree::BaseController is not defined within your app - it is defined in the spree-core gem. If you re-create the filepath to the base controller locally, that is app/controllers/spree/, and copy and paste the code from the controller into a local base_controller.rb, you can edit it and add custom functionality.
Note that it will still inherit from the ApplicationController, but you can place any of the code you wanted to put in the ApplicationController into here and have your classes inherit from Spree::BaseContoller and the effect will be the same.
hmmm, I tried what you want to do but I succeeded (?)
class PagesController < Spree::BaseController
include Spree::Core::ControllerHelpers::Order
end
in the console
2.6.5 :006 > pp PagesController.ancestors
[PagesController,
Spree::Core::ControllerHelpers::Order,
#<Module:0x00007fca27610410>,
Spree::BaseController,
Spree::Core::ControllerHelpers::CurrencyHelpers,
Spree::Core::ControllerHelpers::StrongParameters,
...
I'm using
ruby 2.6.5
rails 6.0.3.4
run bundle update after adding the your spree's gems in the Gemfile
So I think its the requiring or auto-loading problem
what's your rails version? 6? spree >= 4.1 should use rails >= 6
Does Spree::BaseController exist in rails console?
Is Bundler.require(*Rails.groups) in config/application.rb?
Does the gems included in the right group of the Gemfile? ex: spree gems are in :production group.
Does it have config.load_defaults 6.0 in config/application.rb?

Create a rails 5 plugin that need migrations

I want to create a rails 5+ gem that must create a migration as it would have to use a model and save/access data in DB.
My gem has some simple classes I stored in lib/ but it must use some that should access DB and this is what I don't know how to do.
Language should update itself in a DB
Languages should list all existing Languages and create a new Language
Basically in a rails app, I whould create the migrations and models, but I couldn't find tutorials about how to do that, and the rails doc did not help me.
Any help is appreciated.
Take a look at Rails Engines.
With an engine you can define generators, models, routes, controllers, etc and hook into a host rails app.
# routes
MyCustomGem::Engine.routes.draw do
resources :languages
end
# controllers
module MyCustomGem
class LanguagesController < ApplicationController
...
end
end
When installing this in a rails app, you'll use
$ bin/rails my_custom_gem:install:migrations
And that will create the migration in the host app.
If you don’t want to copy the migrate file, you can execute migrate directly in the application as follows
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
initializer :append_migrations do |app|
unless app.root.to_s.match root.to_s
config.paths["db/migrate"].expanded.each do |expanded_path|
app.config.paths["db/migrate"] << expanded_path
end
end
end
end
end
When installing this in a rails app
bin/rails db:migrate

How to use a layout from a gem in rails?

I am making a gem https://github.com/BDMADE/college-admin , It is a simple a layout of an admin template, I am making this gem, for why, I want to re-use in my several rails app.
My welcome controller:
class WelcomeController < ApplicationController
layout 'college-admin/main'
def index
#hello = 'Hello Word'
end
end
My views:
<h1>Welcome#index</h1>
<p>Find me in app/views/welcome/index.html.erb</p>
<%= #hello %>
But when I call from my welcome controller of a demo project, it does shows this error.
So that, My question is how to use my layout(which is laid in college-admin gem) in this controller ?
Should I any change in college-admin gem to display it's layout ?
You need an engine which is a gem with added integration in to the Rails stack.
Engines can be considered miniature applications that provide
functionality to their host applications. A Rails application is
actually just a "supercharged" engine, with the Rails::Application
class inheriting a lot of its behavior from Rails::Engine.
You can create a new engine with:
bundle exec rails plugin new <NAME> --mountable --full --dummy-path=spec/dummy
I got the solution:
Just use this on lib/college/admin.rb:
require "college/admin/engine"
before:
require "college/admin/version"
module College
module Admin
# Your code goes here...
end
end
After the change:
require "college/admin/version"
require "college/admin/engine"
module College
module Admin
# Your code goes here...
end
end
From the controller:
layout 'main'

How to add optional Rails view helpers to my gem?

I'm working on a gem that does some general string manipulations I'd like to expose as helper methods to rails 4+ apps.
Not all consumers of my gem are rails apps so i'd like to safely expose helper methods to rails apps.
Two questions:
How do I add view helper methods to Rails from a gem and where should it live within the gem directory structure?
What can i do to prevent a blow up when the consumer is NOT a rails app? i.e. the gem can't find rails when it's included
Thanks
In your lib/my_gem.rb, you typically want to do something along these lines:
require 'my_gem/action_methods' if defined? ActionView
And lib/my_gem/action_view_methods.rb would contain all if your methods that require Rails/ActionView.
You can add these helpers to Rails with:
module ActionMethods
# ...
end
ActionView::Base.send :include, ActionMethods
Also see this question, and this one.
The rails way is by creating an engine and as it gets loaded with your gem it gets processed by rails
module MyGem
class Engine < ::Rails::Engine
isolate_namespace MyGem
initializer "my_gem.include_view_helpers" do |app|
ActiveSupport.on_load :action_view do
include MyViewHelper
end
end
end
end
Another way you can go is to not include the helper by default so that consumers don't get unexpected side-effects from your gem. Create the helper as a module and document that it should be added to ApplicationController or any needed controller.

Creating gem and trying to reference ApplicationController

I'm creating a gem to create an around_filter on my ApplicationController in all projects that the gem is in. This is my first gem and I'm having a few concerns.
I included rails as a dependency in the gemspec file, but how would I reference anything in rails?
Would I do something like require 'rails' at the top of my file, then do something like
Rails::Path::To::ApplicationController? I understand I could do something like:
Module MyGem
def self.included(klass)
klass.class_eval do
around_filter do |controller, action|
#insert code here
end
end
end
end
Then, in my Rails app I would include it in my Rails ApplicationController, but I don't want to have to include it. Of course, I might have to do some additional config for even that to work, but that is the gist of my crux. How would I reference ApplicationController in my gem? Do I just assume I have Rails since it is a dependency now and I should be able to freely call upon it in my ruby files?

Resources