How to add method that can be used in routes to Gem? - ruby-on-rails

I am building a gem for Rails and would like to make a method available to the routes of the Rails app that uses my gem.
Basically, I want whoever uses my gem to be able to say
websockets_for :messages
which will create a bunch of routes for them. I am unsure where to define that method so that it's available in the routes file.
The method looks something like this:
def websockets_for(resources)
get "/#{resources}", to: "#{resources}#index", as: resources
end
It's basically a helper method I want to make available to generate routes.
I have only found this: https://www.pgrs.net/2007/09/28/add-routes-with-a-rails-plugin-or-gem/
It seems pretty old (from 2007), and I don't think ActionController::Routing is still being used. What's the best way to do this?

Based on Frederick Cheung's comment, I was able to implement this:
require 'action_dispatch/routing'
require 'active_support/concern'
module ActionDispatch::Routing
class Mapper
def websockets_for(resource, &block)
# here, use methods like get, match, etc
get "/#{resources}", to: "#{resources}#index", as: resources
end
end
end
It works! Now, the method websockets_for is available in a Rails app's routes if the gem is installed.

Related

Safe and best way to monkey patch a rails gem

I have tried, seriously. Many questions out there but many developers say "It dont work for me"; I'm one of them -- said to say.
I was reading up on the best way to monkey-patch a rails gem. I've found few but decided to use this method.
I want to monkey-patch the xeroizer gem but rather the invoice.rb model.
# lib/xeroizer/invoice/invoice_url.rb
module Xeroizer
module Invoice
module InvoiceUrl
def invoice_url(id)
#application.http_get(#application.client, "#{url}/#{CGI.escape(id)}/OnlineInvoice")
end
end
end
end
Going with the "this method" link, I assume this should work, but it dosent.
Controller:
include Xeroizer::Invoice::InvoiceUrl
# Invoice.include Xeroizer::Invoice::InvoiceUrl
def some_method
# #xero is in a private method. It's here for short demonstration
#xero = Xeroizer::PrivateApplication.new("MY_CONSUMER_KEY", "MY_SECRET_KEY", "#{Rails.root}/privatekey.pem")
Rails.logger = #xero.Invoice.invoice_url("ad61ea97-b9e9-4a1e-b754-7c19e62f8cd7")
end
undefined method `invoice_url' for Xeroizer::Record::InvoiceModel
How do you add custom methods to a rails gem's class?
Assuming you are trying to monkey-patch Xeroizer::Record::InvoiceModel with Xeroizer::Invoice::InvoiceUrl, you might just do the following right after the first mention of Xeroizer::Record::InvoiceModel (to make Rails to autoload it):
Xeroizer::Record::InvoiceModel.prepend Xeroizer::Invoice::InvoiceUrl
This will override original invoice_url method. The original one still might be called from a prepended using super.

rails path helper not recognized in model

In my rails application I have a teams model. My route.rb file for teams looks like this:
resources :teams
In my teams_controller.rb file the line team_path(Team.first.id) works however the team_path url helper is not recognized in my model team.rb. I get this error message:
undefined local variable or method `team_path' for # <Class:0x00000101705e98>
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.1.1/lib/active_record/dynamic_matchers.rb:26:in `method_missing'
I need to find a way for the model to recognize the team_path path helper.
You should be able to call the url_helpers this way:
Rails.application.routes.url_helpers.team_path(Team.first.id)
Consider solving this as suggested in the Rails API docs for ActionDispatch::Routing::UrlFor:
# This generates, among other things, the method <tt>users_path</tt>. By default,
# this method is accessible from your controllers, views and mailers. If you need
# to access this auto-generated method from other places (such as a model), then
# you can do that by including Rails.application.routes.url_helpers in your class:
#
# class User < ActiveRecord::Base
# include Rails.application.routes.url_helpers
#
# def base_uri
# user_path(self)
# end
# end
#
# User.find(1).base_uri # => "/users/1"
In the case of the Team model from the question, try this:
# app/models/team.rb
class Team < ActiveRecord::Base
include Rails.application.routes.url_helpers
def base_uri
team_path(self)
end
end
Here is an alternative technique which I prefer as it adds fewer methods to the model.
Avoid the include and use url_helpers from the routes object instead:
class Team < ActiveRecord::Base
delegate :url_helpers, to: 'Rails.application.routes'
def base_uri
url_helpers.team_path(self)
end
end
Models are not supposed to be dealing with things like paths, redirects or any of that stuff. Those things are purely constructions of the view or the controller.
The model really should be just that; a model of the thing that you are creating. It should fully describe this thing, allow you to find instances of it, make changes to it, perform validations upon it... But that model wouldn't have any notion of what path should be used for anything, even itself.
A common saying in the Rails world is that if you're finding it difficult to do something (like call a path helper from a model) you are doing it wrongly. Take this to mean that even if something is possible, if it is hard to do in Rails it is likely not the best way to do it.
to add on the previous answer you can use Rails.application.routes.url_helpers just add in route :as like the following example:
get "sessions/destroy/:param_id", as: :logout
so you can use it as following:
Rails.application.routes.url_helpers.logout_path(:param_id => your_value)
Hopefully, this would help

Routes, gems and generators

I have written a gem with an install generator. I would like to use this generator to add routes to the config/routes.rb file, much in the same way as the devise gem does by adding devise_for :model_name. Therefore, I need to know how to:
Make a method (like devise_for) available within the scope of routes?
Ok I've figured it out. To add to the routes file you can use the method route in the generator. I have accomplished this by adding the following to my install_generator.rb file:
def setup_routes
route("add_gem_routes")
end
Note that I am in fact calling a method, which can be added to the scope of routes by defining it in the following namespace:
module ActionDispatch::Routing
class Mapper
def add_gem_routes
#routing code...
end
end
end

How to define own routing helpers in rails 3?

I use polimorphic_path and it some buggy. This method require some route helper that not defined. How can I define (like regular method) own route helper which will be used like "model_name_path, model_name_url etc"?
This solution worked for me.
Add this code to the end of config/routes.rb file. Make sure to replace MyApp with your application's name.
MyApp::Application.routes.named_routes.module.module_eval do
def model_name_path(*args)
# Your code here
end
def model_name_url(*args)
# Your code here
end
end
MyApp::Application.routes.named_routes.instance_eval do
#helpers += [:model_name_path, :model_name_url]
end
These custom methods will be available in controllers, views and tests.
I know one possible answer for _path, but the same isn't working for me for _url. Anybody know why?
# at the bottom of config/routes.rb
module ActionView::Helpers::UrlHelper
def model_name_path model, args={}
# your implementation
end
end

Rails routing - custom routes for Resources

I'm building currently one Rails app and I'd like to stick to all those fancy things like REST and Resources, but I'd like to customise my routes a little. I want my GET route to be little more verbose - the app I'm creating is a simple blog, so instead of GET /posts/1 I'd prefer something like GET /posts/1-my-first-post.
Any ideas how to do this? Didn't find anything on the web.
Routes:
map.resources :posts
Model:
class Post < ActiveRecord::Base
def to_param
"#{id.to_s}-#{slug}"
end
end
Should do the trick.
Btw: http://railscasts.com/episodes/63-model-name-in-url
Define a to_param method in your Model and all the url helpers will youse what you return with that method, e.g.:
class Post < ActiveRecord::Base
der to_param
slug
end
end
You will also need to adapt your controllers for that. Replace:
Post.find(params[:id])
with:
Post.find_by_slug(params[:id])
Also note that the find method raises ActiveRecord::RecordNotFound exception when the record can't be found while using the find_by_* method no Exceptions will be raised so you need to check that manually.
You could find the friendly_id plugin useful as it will also handle redirections if you rename your slugs (thus seo friendly), handles name collisions and seamlessly integrates with the find method so you don't need to touch your controller methods (except for the redirection thingy).
Alternatively...
Add a method like this to post.rb
def path
"/posts/#{id}-#{slug}"
end
Then use the following in your views:
Alternatively...
Add a method like this to application_helper.rb
def permalink(post)
"#{post_path(post)}-#{post.slug}"
end
Then use the following in your views (using permalink(#post) instead of post_path)
<%= link_to #post.title, permalink(#post) %>

Resources