I have a Rails 3.2.8 model that is in a namespace. I'm sure it used to be that the namespace was automatically expanded out into what the table name would look like. e.g. Module::Model would become module_model in the database.
For some reason I have a problem now that this does not happen through the rails application. The migrations, models and controllers all live in a namespace but when the model looks for a table it ignores the table prefix and complains that it cannot find the database.
Here is the example
module Magazine
def self.table_name_prefix
'magazine_'
end
end
module Magazine
class Paper < ActiveRecord::Base
#some stuff here
end
end
When I do a query on Magazine::Paper it looks for the table "paper" and not "maganzine_paper" which it should. This is causing the whole application to exibit some weird behaviour. I have also tried setting the table name manually in the model with self.table_name but this only lets the model find the right table. Routes still play games with me as when I nest something below papers for example comments then it looks for a route that does not exist.
This is what is inside the routes. Comments should go to /magazine/papers/1/comments but it looks for a route called /magazine/comments
namespace :magazine do
resources :papers do
resources :comments
end
end
What is going on?
Update:
Ok so I figured it out but not sure if I should ignore it or see whats causing it. The engine name is magazine and in the engine I create a namespace for models called magazine but this causes the problems. So rails can't have a namespace name similar to the engine name?
I ran into the same problem, forgot my application's name (main module) had the same name as a namespace for my models. Changing either, the module name or the application name solves this issue.
If you just write
namespace :magazine do
resources :papers do
resources :comments
end
end
and run rake routes you can see it is looking for proper urls and when you say it is looking for /magazine/comments then you surely must have written another routes somewhere in the file.
When you write
module Magazine
def self.table_name_prefix
'magazine_'
end
end
And when you write your model with the same module name Magazine then you are replacing the same module with different contents within it. Instead of writing model Paper within Magazine module just include the module within the model Paper in following way.
class Paper < ActiveRecord::Base
include Magazine
#some stuff here
end
This will ensure your table_name_prefix method to be called when your model is loaded when your application starts.
Related
I have an application which defines some models. I want to extend the functionality of some models(eg. adding methods,adding associations) from application to my engine.
I tried adding a model in the engine with the same name as my application's model and Rails will automatically merge them, however it doesn't work.
eg:
(Application's model)
class Article < ActiveRecord:Base
def from_application
puts "application"
end
end
(Inside my Engine)
module MyEngine
class Article < ::Article
has_many :metrics, :class_name => 'Metric'
end
end
has_many association is not getting applied to my Articles model when I try to access #articles.metrics. Any ideas ?
You have the right idea and are close. But your implementation is a little off.
Generally, your engine should have no knowledge of your host app. That way, your engine and the host app(s) stay loosely coupled. So, classes in your engine should not inherit from classes in your host app. (BTW, your approach doesn't work, I believe, because of the way ruby does constant lookups, but that's a different discussion.)
Instead, use the included hook. In the host app, you do something like:
class Article < ActiveRecord:Base
include FooEngine::BarModule
def from_application
puts "application"
end
end
And inside the engine:
module FooEngine
module BarModule
def self.included(base)
base.class_eval do
has_many :metrics, :class_name => 'Metric'
end
end
end
end
When the ruby interpreter goes to include FooEngine::BarModule in Article, it will look for and run (if found) the self.included method in FooEngine::BarModule, passing in the Articleclass as base.
You then call class_eval on the base (Article) which re-opens the Article class so that you can add methods or whatever monkey business you're up to (define new methods in situ, include or extend other modules, etc.).
In your example, you call the has_many method, which will create the various association methods provided by has_many.
If (a) you're going to add a lot of metrics-related functionality through your engine, (b) you want to have lots of classes make use of the metrics-related functionality, and (c) you want some of the functionality to vary from class-to-class (where included), you might consider creating an acts_as_having_metrics (or similar). Once you head down this path, it's a whole new world of wondrous metaprogramming.
Best of luck.
Do you have your metrics model have a belongs_to association with Articles.
You might want to give the other side of the association, Metrics a belongs_to Articles to have this work properly. Also, make sure to have a migration to hold articles_id on the metrics table. Everything should work fine.
I have a Boilerplate model which has two descending models:
BoilerplateOriginal
BoilerplateCopy
While BoilerplateOriginals is sort of a template that admins create, BoilerplateCopys are copies of the originals that are free to edit by everyone, and they also have some more associated objects (e.g. BoilerplateCopy belongs_to: BoilerplateOriginal, BoilerplateCopy belongs_to: Project or BoilerplateCopy has_many: Findings, all of which BoilerplateOriginal doesn't).
So in my opinion, it makes sense to maintain two different model classes that share the same basic functionalities.
Because they also look quite the same, I want to use the same views for them. But under the hood, they are treated a bit different, so I also have two different controllers inheriting from a base controller.
Everything seems to work fine, except that form_for boilerplate, as: resource_instance_name raises an exception undefined methodboilerplates_path', but only when called asnewaction, not when called asedit` action.
Here's what I have done so far to make it work (and everything else seems to work fine):
# config/routes.rb
resources :boilerplate_originals
# app/models/boilerplate.rb
class Boilerplate < ActiveRecord::Base
def to_partial_path
'boilerplates/boilerplate'
end
end
# app/models/boilerplate_original.rb
class BoilerplateOriginal < Boilerplate
end
# app/controllers/boilerplates_controller.rb
class BoilerplatesController < InheritedResources::Base
load_and_authorize_resource
private
def boilerplate_params
params.require(:boilerplate).permit(:title)
end
end
# app/controllers/boilerplate_originals_controller.rb
class BoilerplateOriginalsController < BoilerplatesController
defaults instance_name: 'boilerplate'
end
# app/views/boilerplates/_form.html.slim
= form_for boilerplate, as: resource_instance_name
# ...
As pointed out, new/create works flawlessly, but edit doesn't. And I'm using InheritedResources, by the way.
Rails is dong it correctly, you're slightly doing it wrong
the problem is:
resources :boilerplate_originals
that will just generate routes for especially boilerplate_originals.
when using form_helpers of rails rails will lookup for a route based on the models class which is in this case "boilerplate_copy". that means its looking for a edit_boilerplate_copy_path (which isnt generated by rails)
You said that BoilerplateCopy and BoilerplateOriginal are pretty much looking the same (i guess you just copy a model, there are gems out for doing that for you...)
If you go correct STI it "should" be
class Boilerplate; end
class BoilerplateCopy < Boilerplate; end
class BoilerplateOriginal < Boilerplate; end
for that you need only a route for Boilerplate
resources :boilerplate
and of course a boilerplate_controller
everything is handled as a boilerplate and the form_helper will look up for a new_boilerplate_path, which exists, no matter if its a copy or a original.
I found the problem.
# app/controllers/boilerplate_originals_controller.rb
class BoilerplateOriginalsController < BoilerplatesController
defaults instance_name: 'boilerplate' # This is wrong!
defaults instance_name: 'boilerplate', resource_class: BoilerplateOriginal # This is right!
end
Now a BoilerplateOriginal object is passed to the new action, and not a Boilerplate object.
I'm a bit confused with namespacing in engines. In a Rails engine, where isolate_namespace is used,
module Blog
class Engine < Rails::Engine
isolate_namespace Blorgh
end
end
when is it required that you refer to objects with the namespace Blog (e.g. Blog::Post vs just Post)?
As for example, within the controller of a Post resource of the engine, is it ok to just do Post.find? When it is absolutely required that you use Blog::Post?
Also in models associations, assume that Post has_many :comments. Somehow, I was expecting to define it as follows:
class Post < ActiveRecord::Base
:has_many "blog/comments"
end
since everything is namespaced (models, table names, ...), but it looks like has_many :comments just works. Why namespacing is not used in association keys, and in the case where a Comment resource exists in the host applications, how does rails know which Comment I'm referring to?
When you're inside some module, you can refer to other member of the module without giving the module name, eg:
module Foo
class Bar
def initialize
#baz = Baz.new # same as Foo::Baz.new
end
end
class Baz
end
end
If Baz doesn't exist in the current module, it will cascade down to find the definition, eventually calling const_missing (upon which is built the autoload of classes in Rails), then throw an error if nothing is found.
Rest of your questions is answered here.
I am using Ruby on Rails 3.0.9 and I would like to build a controller name from a class name as well as possible following RoR naming conventions. For example, if I have the Articles::Comment class I would like to retrieve the articles/comments string.
Maybe it exists a RoR method created by developers to handle internally these conventions, but I don't know that.
How can I retrieve the controller name as in the example above?
You're looking for the underscore method. More info here.
"Articles::Comment".underscore
Or, if you've got the controller class itself, it would be like this:
Articles::Comment.name.underscore
EDIT
As of routes, they are built one piece at a time, even when you namespace them. When you do something like this:
map.resources :articles do |articles|
articles.resources :comments
end
What rails is going to do is, first:
"articles". classify # which yields "Article" then rails is going to append "Controller" to it
Then it's going to get "comments" and do the same, but this one is going to be routed under "/articles". Rails does not namespace internal resources, so, the controller has to be CommentsController, not Articles::CommentsController.
Only then you clearly namespace something, Rails is going to namespace your classes:
map.namespace :admin do |admin|
admin.resources :articles # will require controller "Admin::ArticlesController"
end
I hope it's clearer now.
Usually there are a lot of models in a Ruby on Rails project, so:
Is it a good practice to namespace them (in modules/folders)? What are the downsides?
EG:
Shop
category.rb
details.rb
Products
category.rb
base.rb
etc
(instead of ShopCategory, to have Shop::Category?)
Should also the controllers be namespaced in the same manner?
I've recently found this post but back from 2007 by Pratik Naik. Says there namespace in models doesn't really resemble databases. Uses something like below. Even there's a quote from DHH too.
Rails::Initializer.run do |config|
# Your existing stuff
config.load_paths << "#{RAILS_ROOT}/app/models/pets"
end
http://m.onkey.org/2007/12/9/namespaced-models
p/s: I don't know whether the post is still relevant or not, just something I found recently when I wanted namespaces in my models.
I'm doing that a lot.
So yes I think that's something you should do.
It'll be be a lot easier for you to view models if you have them subdivided in subdirectories instead of having them all in the same one.
The same recommendation is also valid for your controllers and your views.
I recommend using single table inheritance for your category model. For example:
Category < ActiveRecord::Base end
ShopCategory < Category end
ProductCategory < Category end
Shop < ActiveRecord::Base
belongs_to :shop_category
end
Product < ActiveRecord::Base
belongs_to :product_category
end
This will encapsulate commonly used category behaviour and attributes into a single model and could allow you to reuse a lot of code and have a single controller. Using namespacing only makes sense to me when the underlying classes have some sort of data/functionality in common. (example: acts_as_versioned creates a Version class namespaced under the model)