Reputation: 1884
I have been wrestling with this for the last day and it's driving me NUTS!
As an learning exercise I decided that I would package up some of my code into a Rails Gem. This code has a controller action, a route, a model and a helper so I decided that the most suitable method of creating a Gem would be to create it as a Rails Engine.
Everything seems to be working well, except for one thing. When I try to reference the Model from within a Controller or Views (of an application that uses the engine) e.g.:
@su = Shortener::ShortenedUrl.generate("http://stackoverflow.com")
I get the following error:
uninitialized constant Shortener::ShortenerHelper::ShortenedUrl
It's strange because the error doesn't happen when I execute the code from the projects console. I think that this is caused by the fact I have put all of the code into the "Shortener" namespace/module. I did this so it would help avoid conflicts when used within other applications.
The code file hierachy looks like this:
And here is the class/module declaration code (with the guts removed) of the important files in question
app/controllers/shortener/shortened_urls_controller
module Shortener
class ShortenedUrlsController < ::ApplicationController
# find the real link for the shortened link key and redirect
def translate
# convert the link...
end
end
end
app/models/shortener/shortened_urls
module Shortener
class ShortenedUrl < ActiveRecord::Base
# a number of validations, methods etc
end
end
app/helpers/shortener/shortener_helper
module Shortener::ShortenerHelper
# generate a url from either a url string, or a shortened url object
def shortened_url(url_object, user=nil)
# some code to do generate a shortened url
end
end
lib/shortener/engine.rb
require "rails/engine"
require "shortener"
module Shortener
class ShortenerEngine < Rails::Engine
end
end
lib/shortener.rb
require "active_support/dependencies"
module Shortener
# Our host application root path
# We set this when the engine is initialized
mattr_accessor :app_root
# Yield self on setup for nice config blocks
def self.setup
yield self
end
end
# Require our engine
require "shortener/engine"
shortener.gemspec
require File.expand_path("../lib/shortener/version", __FILE__)
# Provide a simple gemspec so you can easily use your enginex
# project in your rails apps through git.
Gem::Specification.new do |s|
s.name = "shortener"
s.summary = "Shortener makes it easy to create shortened URLs for your rails application."
s.description = "Shortener makes it easy to create shortened URLs for your rails application."
s.files = `git ls-files`.split("\n")
s.version = Shortener::VERSION
s.platform = Gem::Platform::RUBY
s.authors = [ "James P. McGrath" ]
s.email = [ "[email protected]" ]
s.homepage = "http://jamespmcgrath.com/projects/shortener"
s.rubyforge_project = "shortener"
s.required_rubygems_version = "> 1.3.6"
s.add_dependency "activesupport" , ">= 3.0.7"
s.add_dependency "rails" , ">= 3.0.7"
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
s.require_path = 'lib'
end
I have published the entire code of the engine on GitHub:
https://github.com/jpmcgrath/shortener
NOTE: this engine has a generator to generate the required migration file. Type:
rails g shortener
I have also created a rails 3.1 app that exhibits the problem (look at line 18 of the projects controller):
https://github.com/jpmcgrath/linky
Any ideas? I have scoured the web, but have not been able to find any really definitive guide to making Engine Gems. Any helpers would be much appreciated.
Thanks!
Upvotes: 5
Views: 3219
Reputation: 13675
In your engine helper (app/helpers/shortener/shortener_helper.rb
), replace both occurrences of ShortenedUrl
with Shortener::ShortenedUrl
.
I found this error weird at the beginning, because ruby is supposed to look for constants in the enclosing module. But helpers are included in another class, which could mean that the constant name resolution environment isn't the same as the one you see in the file.
If you want to know more about namespaced engines and their behaviour, you can look at this excellent answer.
Upvotes: 3