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
Related
I have a /lib/custom
inside I have custom.rb and custom_page.rb
custom.rb
require 'custom_page.rb'
module Custom
def self.name(params)
# logic
end
end
I've added in application.rb config.autoload_paths += %W(#{config.root}/lib)
I can't seem to call in my controllers to Custom.name(params)
NoMethodError: undefined method `name' for Custom:Module
I've tried with define the method as def Custom.name, using class << self and method_function :name yet nothing helps..
Am I missing something?
It's because of the Rails naming convention. In your rails console, try
irb(main):001:0> Custom::Custom
LoadError: Expected lib/custom/custom.rb to define Custom::Custom
Rais expects you do define module Custom::Custom (not module Custom) in lib/custom/custom.rb.
Rails sees a folder lib/custom and created an empty module Custom (doesn't respond to name method) based on convention, if you want to define module Custom, you have to write a file lib/custom.rb
The convention is
lib/custom.rb #define module Custom
lib/custom/deeper.rb #define module Custom::Deeper
lib/empty_folder/ # rails provides you an empty module EmptyFolder
BTW you don't have to require 'custom_page' in your custom.rb, if Rails sees CustomPage in your code, it will try to load the class definition file based on naming convention, given that your custom_page.rb file path follows the convention.
You can also use ActiveSupport::Concern to extend both class and instance methods. In the module just to this:
module Custom
extend ActiveSupport::Concern
included do
# everything but the class methods go here
end
module ClassMethods
# define class methods here
def name(params)
#logic
end
end
end
I need to monkey-patch one of the Rails core classes, specifically ActionView::Helpers::UrlHelper::ClassMethods.link_to method. As far as I remember there are some events fired when parts of Rails are loaded, how to add handlers for them? Or should I just put the code into initializer?
link_to does not appear to be in ClassMethods. From here.
In config/initializers/url_helper_extensions.rb
module ActionView
module Helpers
module UrlHelper
alias_method :_link_to, :link_to
def link_to
# Your code ...
# Call original method if you want.
_link_to
end
end
end
end
I have a module called Sms that I'm defining in lib/sms.rb. Within it, I have a method called Sms.chunk that uses the method word_wrap. This is part of the TextHelper library, so I am including it at the beginning of the module with include ActionView::Helpers::TextHelper:
module Sms
include ActionView::Helpers::TextHelper
def Sms.chunk
...
word_wrap
...
I am requiring this module during initialization with the line require "sms" in config/initializers/additional_libs.rb
I also have a Grape API class called TWILIO_API where I want to call Sms.chunk. However, when I do, I get undefined methodword_wrap' for Sms:Module`. I have tried including the TextHelper library in the TWILIO_API class itself, and various other ways of including it, but have had no success.
What am I doing wrong here?
The problem is that wrap_word is an instance method , and you're invoking it from a class method Sms.chunk. Make it an instance method by dropping the Sms. part. For instance, the following works:
require 'action_view'
class Test
include ActionView::Helpers::TextHelper
def test_method
word_wrap('Once upon a time')
end
end
o = Test.new
p o.test_method # "Once upon a time"
I have a method defined in application_helper.rb:
def bayarea_cities
[
['San Francisco', 'San Francisco'],
['Berkeley', 'Berkeley'],
...
]
end
I'm also using Grape to create an API. It's in its own module outside the Rails app:
module FFREST
class API_V2 < Grape::API
...
I'm pretty sure Grape is a Rack app, so it doesn't have normal access to the Rails modules. When I try to call the 'bayarea_cities' method in one of the API methods, I get an undefined variable or method error. I've tried include the ApplicationHelper module with 'include ApplicationHelper', but this did not work.
How can I get access to this inside the API class?
UPDATE:
Thanks for the update Deefour. I added extend self to my Helpers module, and referenced the methods as instance/mixin methods (not as module methods), but I'm still getting the same error. In my lib/helpers.rb file I have:
module Helpers
extend self
def bayarea_cities
[
'San Francisco',
'Berkeley',
'Danville',
'Oakland',
'Daly City',
'Sunnyvale'
]
end
def us_states
['CA']
end
end
and in my API file I have:
module FFREST
class API_V1 < Grape::API
include Helpers
version 'v1', :using => :header, :vendor => 'feedingforward'
...
And of course, I have the config/initializers/helpers.rb file that says require "helpers"
But when I call the US states API method, for instance, by going to http://localhost:5000/api/states, I get:
undefined local variable or method `us_states' for #<Grape::Endpoint:0x007fd9d1ccf008>
Any ideas?
Create some lib/helpers.rb file with the contents: module Helpers; end
Move the bayarea_cities method into this module definition
Add a config/initializers/helpers.rb file containing require "helpers"
Inside the ApplicationHelpers class, add include Helpers
Inside your API_V2 class add include Helpers
You'll now have told Rails to make the Helpers module available within your application, and made bayarea_cities available as a method within both your Grape API class and your Rails app. The above are steps simply to get the point across - you need to put this common functionality in a place it can be easily accessed by any part of your application. You can (and should) use namespace your Helpers module.
Another tip: add extend self to the module to avoid the need to define everything as class methods as you mentioned in the comment
module Helpers
extend self
def bayarea_cities
#...
end
end
Finally, if you're including the module properly with include Helpers, you should be able to acces the method simply as bayarea_cities, not Helpers.bayarea_cities. If this isn't the case, you should definitely show the error you get so we can sort that out for you.
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