I may write more about my experiences with rails engines but for those who haven't used this (awesome) feature yet a rails engine is basically a gem (or plugin... for now) that not only interacts with a rails application, but can inject its own controllers, models, views and other standard rails components into your application.
As part of this CMS I want to dynamically map routes to the controller path in the gem that handles displaying a page. So if the user creates a page called Foo with a "slug" of foo/bar I want to dynamically bind a route like:
get "/foo/bar", :to => "cmsgem/pages#display"
This should happen when a page is published (if the route doesn't already exist.) It should also happen when the application spins up. It should look at all published pages and bind them.
To do this you can define an initializer in your main rails engine class. It looks something like:
module Cmsgem
class Engine < ::Rails::Engine
initializer 'cmsgem.bind_dynamic_routes', :after => :disable_dependency_loading do |app|
app.routes.draw do
Cmsgem::Page.where(:published => true).each do |p|
get "/#{p.slug}", :to => "cmsgem/pages#display"
end
end
end
end
end
(note: if you want to know why i have the :after => :disable_dependency_loading check this article)
The problem I ran into was, how do I test this? This code executes when you initialize rails. Rspec spins up it's access to rails prior to running your tests, which means I can't create a Page and test for the existence of that route, because the page would be created after initialization and not get referenced when the code block above runs.
So I dug into the routing API a bit, and came up with this:
describe Cmsgem::Engine do
it "dynamically adds routes from pages at initialization" do
page = Cmsgem::Page.create(:slug => "rspec/test", :title => "Rspec Test", :content => "This is an rspec test")
initializer = Cmsgem::Engine.initializers.select { |i| i.name == "cmsgem.bind_dynamic_routes" }.first
initializer.run(Rails.application)
route = Rails.application.routes.recognize_path("/rspec/test", :method => :get)
route.should_not be_nil
end
end
Basically what's happening is that I'm finding my custom initializer in the collection of initializers that my gem runs, then explicitly rerunning it. You have to pass in the rails application as it is used to bind the routes in the gem.
I'd be curious if there's a better solution to this problem. Maybe some way to setup a record in rspec pre-initializer or a better way to reinitialize rails. If anyone has any suggestions let me know. Hopefully I'll post more about this gem as I go along.
This comment has been removed by the author.
ReplyDeleteIt is very nice blogger Thank you porovide importent information and i am searching for same information to save my time.Ruby on Rails Online Training
ReplyDeleteMMORPG
ReplyDeleteinstagram takipçi satın al
Tiktok Jeton Hilesi
tiktok jeton hilesi
SAC EKİMİ ANTALYA
Takipci Satın Al
instagram takipçi satın al
METİN2 PVP SERVERLER
instagram takipçi satın al
kartal bosch klima servisi
ReplyDeleteataşehir arçelik klima servisi
ümraniye bosch klima servisi
pendik bosch klima servisi
pendik arçelik klima servisi
tuzla samsung klima servisi
çekmeköy vestel klima servisi
ataşehir vestel klima servisi
çekmeköy bosch klima servisi