I am developing a rubygem specifically for Rails applications and I want to add a controller from my gem so that it will be available on the Rails app(Similar to what devise does with RegistrationsController, SessionsController).
On the gem side:
I've tried adding the following
app/controllers/samples_controller.rb
class SamplesController < ApplicationController
def index
.
.
end
end
And then on my rails routes add it either as:
match 'route' => 'samples#index'
or
resources :samples
Clearly I got something wrong over there but I have no idea what is it? Do I need to explicitly require my SampleController somewhere or an initializer on the app?
Right now I am getting this error when accessing the route
uninitialized constant SamplesController
Thanks :)
Let's assume your gem is called MyGem and you have a controller called SamplesController that you want to use in the app. Your controller should be defined as:
module MyGem
class SamplesController < ApplicationController
def whatever
...
end
end
end
and in your gem directory it should live at app/controllers/my_gem/samples_controller.rb (not under the lib folder).
Then create engine.rb in your gems lib/my_gem folder with code
module MyGem
class Engine < Rails::Engine; end
end
You can write routes inside your gem by writing creating routes.rb in config folder with code
# my_gem/config/routes.rb
Rails.application.routes.draw do
match 'route' => 'my_gem/samples#index'
end
Final structure something like this
## DIRECTORY STRUCTURE
#
- my_gem/
- app/
- controllers/
- my_gem/
+ samples_controller.rb
- config/
+ routes.rb
- lib/
- my_gem.rb
- my_gem/
+ engine.rb
+ version.rb
+ my_gem.gemspec
+ Gemfile
+ Gemfile.lock
Thats it.
First of all you have a typo in your code: AppicationController should be ApplicationController.
Then, you're not following the Rails naming conventions (plural for resources etc.):
In your routes it would have to be either resources :samples or resource :sample.
Your controller class should be class SamplesController and
the filename of the controller should be samples_controller.rb.
Follow the conventions and you should be fine.
to set up your route, create a routes.rb file in the config directory of your project. To have it match on the sample route, do the following:
config/routes.rb
Rails.application.routes.draw do
<resource definition here>
end
app/controllers/samples_controller.rb
module Samples
class SamplesController < ApplicationController
def index
.
.
end
end
end
Remember to include the module in the application controller
include 'samples'
Have you looked at this site:
http://coding.smashingmagazine.com/2011/06/23/a-guide-to-starting-your-own-rails-engine-gem/
Related
I'm refactoring my companies routes file following this SO post to look like the following.
config/application.rb
module YourProject
class Application < Rails::Application
config.autoload_paths += %W(#{config.root}/config/routes)
end
end
config/routes/admin_routes.rb
module AdminRoutes
def self.extended(router)
router.instance_exec do
namespace :admin do
resources :articles
root to: "dashboard#index"
end
end
end
end
config/routes.rb
Rails.application.routes.draw do
extend AdminRoutes
end
However much of our newer RoR code we put into appsules, which are self contained little pieces of the application that contain their own controllers, models, serializers, etc, and someone mentioned how nice it would be if they also contained their own routes. The path to that would look like the following
/appsules/#{appsule_name}/routes.rb
But when I look at the config.paths in my application.rb I don't see any paths pertaining to the appsules directory. Is this possible to read in routes files in that fashion?
Are you updating the autoload path to use the new folder structures? Something like:
module YourProject
class Application < Rails::Application
config.autoload_paths += %W(
#{config.root}/config/routes
#{config.root}/appsules/appsule1_name/routes.rb
#{config.root}/appsules/appsule2_name/routes.rb
)
end
end
If you want them added dynamically, you should be able to iterate the appsule directory and add these files dynamically to the autoload path.
I'm new in Ruby and RoR and I'd like to create an admin section for a demo app.
From a little research I've done I've found two different options for creating an admin section. Example:
# config/routes.rb
namespace :admin do
resources :users
end
# app/controllers/admin_controller.rb
class AdminController < ApplicationController
before_filter :authorized?
...
end
Which of the two options is the most proper way to define controllers for the admin section, or they are both equally same?
# app/controllers/admin/users_controller.rb
# I think this is what rails generates if I run the "rails g controller admin::users" command
class Admin::UsersController < AdminController
...
end
# or instead
module Admin
class UsersController < AdminController
....
end
end
Both approaches yield to the same result, which is an UsersController which inherits from AdminController and is found in the Admin module (namespace).
Admin::MyClass is just a shortcut for module Admin ... class MyClass, but...
I would however prefer the explicit nested code (with module Admin on its own line), because it does make a different if the Admin-module has never been defined before. This probably won't happen to you when hacking with standard rails, but can happen when you write ruby code outside of rails.
See these examples:
class I::Am::AClass
end
i = I::Am::AClass.new
puts i.inspect
will lead to
i.rb:1:in `<main>': uninitialized constant I (NameError)
if you never declared the I and nested Am modules before in your code.
Whereas
module I
module Am
class AClass
end
end
end
i = I::Am::AClass.new
puts i.inspect
will work:
#<I::Am::AClass:0x00000001d79898>
because the modules are created along the path to AClass (at least this is how I think about it).
If you ever run in that problem and want to save whitespaces (because you will usually indent stuff in a module definition), there are some idioms to use. The one that solves the problem in the most obvious way (again, to me) is the following:
# Predefine modules
module I ; module Am ; end ; end
# Just a shortcut for:
# module I
# module Am
# end
# end
class I::Am::AClass
end
i = I::Am::AClass.new
puts i.inspect
#<I::Am::AClass:0x000000024194a0>
Just found that the nature of your question (it is not about an Admin-Interface, more about Module-Syntax) is also nicely discussed here Ruby (and Rails) nested module syntax . And I would love to see a ruby-bug report/feature-request on this :)
You can also use the administration framework for Ruby on Rails applications like
ActiveAdmin https://github.com/activeadmin/activeadmin
OR
Railsadmin
https://github.com/sferik/rails_admin
I have a namespaced Post controller as below
class Admin::Blog::PostsController < Admin::BaseController
end
and a namespaced model as follows.
class Blog::Post < ActiveRecord::Base
end
But when I try to access the model inside the index action of the post controller as below
def index
#posts = Blog::Post.where(:foo_id => params[:id]).paginate(:page => params[:page], :per_page => 20)
end
I get the following error
LoadError at /admin/blog/posts
Expected/app/models/blog/post.rb to define Post
But when I move the model to Admin::Blog::Post namespace from Blog::Post is works.
I'm bit confused with this and not able to get what is going on with this.
Is it required that Controller and Model should be present in the same namespace ?
Following is the snippet from routes.rb
namespace :admin do
namespace :blog do
resources :posts
resources :categories
end
end
Blog module snippet
module Blog
def self.table_name_prefix
'blog_'
end
end
Preloading controllers and models
config.autoload_paths += Dir["#{Rails.root}/app/models/**/**"]
config.autoload_paths += Dir["#{Rails.root}/app/controllers/**/**"]
config.autoload_paths += Dir["#{config.root}/app/helpers/**/**"]
config.autoload_paths += Dir["#{config.root}/app/tags/**/**"]
config.autoload_paths += %W[ #{Rails.root}/app/extensions #{Rails.root}/app/modules #{Rails.root}/app/drops #{Rails.root}/app/filters #{Rails.root}/app/mailers ]
This is probably caused by rails' autoloader. When doing this :
module Foo
class Bar
end
end
And then trying to use Foo::Bar, the autoloader first tries to locate app/models/foo/bar.rb. The file is loaded, and module Foo is defined here (albeit as a module containing solely Bar) so the autoloader never attempts to load app/models/foo.rb.
This should only happen in development mode, as in production mode all of your files are require'd on startup.
There are two workarounds AFAIK :
Require the module
using require_dependency :
require_dependency 'foo'
module Foo
class Bar
end
end
This is IMHO the right solution, as it does not break the constant lookup, but it is also a bit annoying as you have to add the require statement on top of each namespaced file.
Create Custom Active record Base
This solution doesn't rely on autoloading. Set the models to inherit from the following, instead of from ActiveRecord::Base directly:
class CustomActiveRecordBase < ActiveRecord::Base
self.abstract_class = true
# If no table name prefix has been defined, include the namespace/module as
# table name prefix, e.g., Blog:: -> blog_
def self.table_name
# If a table_name_prefix has been defined, follow default behaviour
return super if full_table_name_prefix.present?
# Find the prefix, e.g., Blog::Post -> 'blog', User -> ''
prefix = model_name.name.deconstantize.underscore
# If no prefix, follow default behaviour
return super unless prefix.present?
# Otherwise add the prefix with an underscore
"#{prefix}_#{super}"
end
end
Then there is no need to define self.table_name_prefix in blog.rb.
This could all be done by monkey-patching ActiveRecord::Base, but this interferes with other classes, such as ActiveRecord::SchemaMigration, which doesn't have a table prefix.
Note :
This bug seems to have been resolved in rails 4. I used the second workaround a lot while on rails 3, but I've tried to reproduce the bug in rails 4 and it does not show up anymore. I think they modified the way the autoloader works... For more info, see the rails guides on autoloading and reloading constants
In my Rails app,I have a controller /app/api/mem_controller.rb
class MemController < AplicationApiController
before_filter :mem_login?
def follows
_mem = MemAccount.find(params[:id])
render json: {
:items=> _mem.follow_mems.limit(page_size).offset(page * page_size),
:count=> _mem.follow_mems.length
}.as_json(:methods=>['avatar_url'])
end
end
I add a config in application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
My route is:
namespace :api do
get "mem/:id/follows" => 'mem#follows'
end
Now I want the route is /api/mem/1/follows.
But this raise error:
uninitialized constant Api
If I take out the namespace wrapper,/mem/1/follows will do work.
Then I want to know how can I realize /api/mem/1/follows throuth the route keywords namespace,I need the api prefix to avoid the conflict.
I don't want to place the api folder under /app/controller/
yes sure, because you are using namespace.
try this:
class Api::MemController < AplicationApiController
...
end
and that controller MemController should be:
app/controllers/api/mem_controller.rb
if you don't want to create sub-folder, then you should use scope instead of namespace in routes.rb, and in that case you can keep your MemController without changing (I mean you don't need to add Api::)
scope '/api' do
end
more explanation: http://guides.rubyonrails.org/routing.html
I'm working on an infrastructure for Rails application and I'm trying to take something out of someones existing project.
I am sort of new to rails but I read the guides on plugins and engines ect..
So I have a gemified Engine, containing some module. I have a model say SharedPost trying to extend said module and I'm getting the uninitialized constant error
uninitialized constant Stake::SharedPost
Here's some of what my engine looks like:
#file: lib/stake/shared_post.rb
module Stake
module SharedPost
...
end
end
#file: lib/stake/engine.rb
module Stake
class Engine < ::Rails::Engine
isolate_namespace Stake
end
end
And in the main app I've got
#file: Gemfile
...
gem 'stake'
...
#file: config/routes.rb
Pop::Application.routes.draw do
root :to => 'home#index'
mount Stake::Engine, :at => '/stake'
end
#file: app/models/posted.rb
class Posted < ActiveRecord::Base
extend Stake::SharedPost
...
end
end
The main application will load, though with no available data on it.
While I try to run
rake db:seed
for example when trying to load the Posted model I get the error uninitialized constant Stake::SharedPost
What am I missing to get access to my gem's namespaced modules?
EDIT:
I've read into the acts_as pattern and that doesn't seem to be the cleanest way of doing things, plus I'm not sure how to implement that onto my engine. Is there another solution?
In lib/stake.rb are you including the lib/stake/shared_post.rb file?
It should look something like this:
# file lib/stake.rb
require "stake/shared_post.rb"
module stake
....
end