Writing Rails Gems: Proper way to add to existing rails modules? - ruby-on-rails

I'm just getting started with rails gems, and wondering how best to add functionality to existing rails modules. For example, if I wanted to add a new form helper, I'd typically do something like this:
class ActionView::Helpers::FormBuilder
# My form defs in here
end
But I'm wondering if that's the most elegant way of doing things - especially if, for example, I'm going to wrap the new functionality up in a gem.
For example, suppose I'm creating the gem "MyGem", and I only want its functionality to be present if the gem is called in the controller. So in the controller I add 'include MyGem', and in the lib/my_gem.rb I'd typically do something like:
# lib/my_gem.rb
module MyGem
# My form defs in here
end
The question is: what is the standard way for overwriting defs in the ActionView::Helpers::FormBuilder module from within the MyGem module?
Cheers...

If you create a Class with inheritance of ActionView::Helpers::FormBuilder you can override all method from FormBuilder you want.
You can add other method too.
After you just need use this FormBuilder when you create your form with option :builder Or you can do an helper method like simple_form_for to call the form_for method with your builder.
If you want do in a module you need create your class in module
# lib/my_gem.rb
require 'my_gem/form_builder'
# lib/my_gem/form_builder.rb
module MyGem
class FormBuilder < ActionView::Helpers::FormBuilder
# form def
end
end
In your builder you use :builder => MyGem::FormBuilder

Related

Creating a Gem that adds a rails form helper

I'm creating a gem to add a new helper method for rails forms. My gem is a single file
lib/rails_json_field.rb
that looks like this:
require 'action_view/helpers'
require 'action_view/context'
require 'securerandom'
module ActionView
module Helpers
class FormBuilder
include ActionView::Helpers::FormTagHelper
include ActionView::Helpers::JavaScriptHelper
include ActionView::Context
def json_field_tag(method, options = {})
#function code here
end
end
end
end
ActiveSupport.on_load(:action_view) do
include ActionView::Helpers::FormBuilder
end
However when I use the method like so:
= f.json_field_tag(:some_method)
I receive the following error:
ActionView::Template::Error (undefined method `json_field_tag' for #<ActionView::Helpers::FormBuilder:0x007ffa84ab52a8>)
How do I make the method available on ActionView::Helpers::FormBuilder ?
You have defined the following class:
RailsJsonField::ActionView::Helpers::FormBuilder
You meant to monkeypatch the following class:
ActionView::Helpers::FormBuilder
That's why the error message is telling you the method is undefined; you have defined it within a class within your custom module, not within the specified class:
undefined method `json_field_tag' for #<ActionView::Helpers::FormBuilder
It's only defined in RailsJsonField::ActionView::Helpers::FormBuilder, so you get the above error.
If you want to properly monkeypatch the original code then you should look at the original code to ensure your code looks like their code:
module ActionView
module Helpers
class FormBuilder
def json_field_tag(method, options = {})
# function code here
end
end
end
end
It would be better to define this as an initializer in your Rails app, e.g., in config/initializers/json_field_tag.rb. Once you have the code working as a simple patch, then you can focus on developing it into a standalone gem that enhances ActionView.
After searching, I found a different gem that adds a FormBuilder method. I used their repo as a guide to structure my own. For others with this questions, you can view my repo and their repo here respectively:
https://github.com/dyeje/rails_json_field
https://github.com/Brantron/john_hancock

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.

Rails undefined method for Module

In Rails, how do you use a specific method from a module. For eg,
# ./app/controllers/my_controller.rb
class MyController < ApplicationController
include MyModule
def action
MyModule.a_method
end
private
def a_method
...
end
end
# ------------------------------------------------ #
# ./app/helpers/my_module.rb
module MyModule
def a_method
...
end
end
MyController includes MyModule. And in action ,I want to use MyModule.a_method (Please note I also have a private a_method in MyController and I don't want to use this.)
Things I've tried :
1) Defining the method in the module as self.
def self.a_method
end
2) Using the :: notation in controller (MyModule::a_method)
The error that I keep getting is
Undefined method:a_method for MyModule:module
For now, I've resorted to using a different name for the modules method. But I'd like to know how to namespace the function with either the Module:: or Module. notation
[UPDATE - 11/24/2014]
adding file structure in code, since Rails heavily relies on convention.
So I am not really sure what you are trying to accomplish with your module but a quick solution to get it working is below.
Move my_module.rb out of helpers and into lib/my_module.rb. The helpers directory is for methods that you use in your views. The convention is to utilize helpers that are namespaced after their respective controller or the application_helper.rb for global methods for your views. Not sure if that's what you are trying to accomplish with your module but wanted to throw that out there.
Create an initializer (you can all it whatever) in config/initializers/custom_modules.rb and add require 'my_module'
Update the a_method back to be self.a_method
You can now call MyModule.a_method in your app
Don't forget to restart your server for changes to lib/my_module.rb to take effect.
Also, a lot of people reference this post by Yehuda Katz as guidance on where to store code for your app. Thought it might be a helpful reference.
if you include MyModule into MyController, all the "instance methods" of the first will be mixed-in into the 2nd.
So if you only want to call MyModule.a_method, no need to include your module.
Then you'd want to require (or better autoload) your module before using it. To do so place it in controllers/concerns/my_module.rb, rails (4 at least) should autoload it, otherwise require its file in an intializer
# my_module.rb
module MyModule
def self.a_method
...
end
end
should work, but doing
# my_module.rb
module MyModule
extend self
def a_method
...
end
end
is more clean to me. You'd like to have a look to rails active support concern to understand the "rails way" on this topic.

What's the right way to make new methods available to views from a Rails Gem?

If I want to create a new rails gem that adds methods to Rails views what is the right way to do this? Is it to extend ActionView::Base? Would it involve ApplicationHelper in some way?
Many gem authors create a module that defines their view helper methods and then includes them in ActionView::Base.
module MyGem
module ActionViewExtensions
module MyHelpers
def my_view_helper
# ...
end
end
end
end
# You can do this here or in a Railtie
ActionView::Base.send :include, MyGem::ActionViewExtensions::MyHelpers
Railtie method:
https://github.com/mynameisrufus/sorted/blob/master/lib/sorted/railtie.rb
Alternative:
https://github.com/plataformatec/simple_form/blob/master/lib/simple_form/action_view_extensions/form_helper.rb

how can i use a helper in different views

i am using refinery cms at the moment. I created an engine and with it some helpers in app/helpers/admin/.
now i would like to use those helpers in my frontend view (ie. app/views/myapp/index) as well. but i can not...undefined methode error.
what do i have to do short of copying the whole thing to app/helpers/?
the helper looks like this
module Admin
module myHelper
def somefunc
end
end
end
so is it possible to use somefunc outside of the Admin module?
The "Rails way" to include a helper from a non-standard path in a view is to use the .helper method within your controller.
class MyController < ApplicationController
helper Admin::MyHelper
...
end
http://apidock.com/rails/AbstractController/Helpers/ClassMethods/helper
In your application_helper.rb:
module ApplicationHelper
include Admin::MyHelper
end
This will import those helper methods into the ApplicationHelper, thus making them available in your views. You could do this in any of your helpers really.
You might try to use the full object reference like Admin::myHelper::somefunc to call somefunc from outside the Admin module.

Resources