Ruby on Rails Updating Heroku Dynamic Routes

Ruby On Rails Updating Heroku Dynamic Routes

Fixed it!

There is something called an "ActiveRecord Observer" which have been depreciated since Rails 4.0. I found this website which explained what I wanted to do, but it was slightly outdated. I've included by code for rails 4.0 below:

If you're using Rails 4, use the rails-observers gem

Add the call to your environment files:

#config/application.rb (can be placed into dev or prod files if required)
config.active_record.observers = :slug_observer

Add a new observer class into your models folder:

#app/models/slug_observer.rb
class SlugObserver < ActiveRecord::Observer
def after_save(slug)
Rails.application.reload_routes!
slug.logger.info("Routes Reloaded")
end

def after_destroy(slug)
Rails.application.reload_routes!
slug.logger.info("Routes Reloaded")
end
end

The way this works is to call these bolt-on functions once the original has been run. This allowed my app to operate the function indepenent of the model in question, thus updating the routes correctly.

Is it a bad idea to reload routes dynamically in Rails?

Quick Solution

Have a catch-all route at the bottom of routes.rb. Implement any alias lookup logic you want in the action that route routes you to.

In my implementation, I have a table which maps defined URLs to a controller, action, and parameter hash. I just pluck them out of the database, then call the appropriate action and then try to render the default template for the action. If the action already rendered something, that throws a DoubleRenderError, which I catch and ignore.

You can extend this technique to be as complicated as you want, although as it gets more complicated it makes more sense to implement it by tweaking either your routes or the Rails default routing logic rather than by essentially reimplementing all the routing logic yourself.

If you don't find an alias, you can throw the 404 or 500 error as you deem appropriate.

Stuff to keep in mind:

Caching: Not knowing your URLs a priori can make page caching an absolute bear. Remember, it caches based on the URI supplied, NOT on the url_for (:action_you_actually_executed). This means that if you alias

/foo_action/bar_method

to

/some-wonderful-alias

you'll get some-wonderful-alias.html living in your cache directory. And when you try to sweep foo's bar, you won't sweep that file unless you specify it explicitly.

Fault Tolerance: Check to make sure someone doesn't accidentally alias over an existing route. You can do this trivially by forcing all aliases into a "directory" which is known to not otherwise be routable (in which case, the alias being textually unique is enough to make sure they never collide), but that isn't a maximally desirable solution for a few of the applications I can think of of this.

Dynamic Rails routes based on database models

There is a nice solution to that problem using routes constraints.

Using routes constraints

As the rails routing guide suggests, you could define routes constraints in a way that they check if a path belongs to a language or a category.

# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Language.where(name: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Category.where(name: request[:category]).any? }

The order defines the priority. In the above example, if a language and a category have the same name, the language wins as its route is defined above the category route.

Using a Permalink model

If you want to make sure, all paths are uniqe, an easy way would be to define a Permalink model and using a validation there.

Generate the database table: rails generate model Permalink path:string reference_type:string reference_id:integer && rails db:migrate

And define the validation in the model:

class Permalink < ApplicationRecord
belongs_to :reference, polymorphic: true
validates :path, presence: true, uniqueness: true

end

And associate it with the other object types:

class Language < ApplicationRecord
has_many :permalinks, as: :reference, dependent: :destroy

end

This also allows you to define several permalink paths for a record.

rails_category.permalinks.create path: 'rails'
rails_category.permalinks.create path: 'ruby-on-rails'

With this solution, the routes file has to look like this:

# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Permalink.where(reference_type: 'Language', path: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Permalink.where(reference_type: 'Category', path: request[:category]).any? }

And, as a side note for other users using the cancan gem and load_and_authorize_resource in the controller: You have to load the record by permalink before calling load_and_authorize_resource:

class Category < ApplicationRecord
before_action :find_resource_by_permalink, only: :show
load_and_authorize_resource

private

def find_resource_by_permalink
@category ||= Permalink.find_by(path: params[:category]).try(:reference)
end
end

rails 5 dynamic routing redirections based on model attributes

Found a solution on this post here.

I need to restart the server so newly created route get loaded after a Place object creation. Not sure if I am doing it right though but it works..

#config/route.rb

Rails.application.routes.draw do
Place.all.each do |pl|
get "/#{pl.shortlink}" => redirect("/users/sign_up?q=#{pl.id}&t=#{pl.token}")
end
end

In order to load the newly created route when adding a Place, I added

#models/place.rb

after_create :add_shortlink
...
def add_shortlink
Rails.application.reload_routes!
end

This will not work on Heroku, issue addressed is here

Dynamic segment in resourceful route

You can jump on to the console and type

$ rake routes

This will tell you the named routes for all of your possible routes. This particular route will not be in there as you have not named it. You need the :as param

match '/confirm/:token' => 'users#confirm', :as => :confirm

Then the route will probably be (you can confirm with rake routes):

users_confirm_path(user_id, token)

Rails.application.routes.url_helpers crashing Heroku app

I had this same problem and found that is was caused due to 2 reasons:

First, using the Rails.application.routes.url_helpers methods in class constants like in the following snippet:

class myObject
include ActiveModel::Model

attr_accessor :id, :link

DEFAULT_OBJECT = new id: 1,
link: Rails.application.routes.url_helpers.my_object_url({:id => 1})

...
end

Second, setting config.eager_load = true in the environment file.

So result being, when config.eager_load = true, the dynamic url helpers haven't been registered when the myObject class is loaded.

This article maybe useful -> http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload/

Route all subdomains to my Heroku Rails app

In DNS setups, there are usually two parts to the equation: (1) the sending side, which is the DNS server that will direct the traffic to the server, and (2) the receiving side, which is the server being sent the traffic to, that often needs to know on which domains to listen.

The Heroku documentation on Wildcard domains is pretty clear:

Add a wildcard domain to your app as you would with any other domain, but use the * wildcard subdomain notation.

$ heroku domains:add *.example.com
Adding *.example.com to example... done

The Heroku router is ready to receive your traffic. You just need to have your DNS point there. So, in your DNS—probably hosted at your registrar in your case—add your wildcard record:

*.example.com.  CNAME   example-1234.herokuapp.com

(or whatever Heroku tells you to put as CNAME)



Related Topics



Leave a reply



Submit