Rails 3 namespacing requires model to be defined twice? - ruby-on-rails-3

I'm pulling my hair out trying to understand namespacing in Rails 3. I've tried following a few different tutorials, and the only way I can get my models to work is if I define my model in both the base directory and my namespace directory.
If I only define the model in the namespace directory it expects it to define both Model and Namespace::Model, as below:
LoadError (Expected .../app/models/plugins/chat.rb to define Chat):
or
LoadError (Expected .../app/models/plugins/chat.rb to define Plugins::Chat):
I'm sure I'm missing something obvious, but I could really use a pointer in the right direction.
Here are the relevant excerpts.
/models/plugins/chat.rb
class Plugins::Chat
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
...
end
/controllers/plugins/chats_controller.rb
class Plugins::ChatsController < Plugins::ApplicationController
load_and_authorize_resource
...
end
/config/routes.rb
namespace :plugins do
resources :chats
end
/config/application.rb
config.autoload_paths += Dir["#{config.root}/app/models/**/"]
Edit
This is some kind of bad interaction with CanCan, the gem we're using for permissions. The line load_and_authorize_resource is somehow at fault. Will keep digging...

I noticed a reference to load_and_authorize_resource in your controller. This method is used by the CanCan gem to create an instance of your model and then test if the user has access to it. If you are using a namespaced model you will need to specify the class:
class Plugins::ChatsController < Plugins::ApplicationController
load_and_authorize_resource :class "Plugins::Chat"
...
end

It sounds like at some point you're referencing the Chat constant \by itself before it's loaded. Rails then tries to find that by looking at models/chat.rb, can't find it, and complains. Check your constant usage (the backtrace should tell you where it's being invoked from), and clean it up, and Rails should be less complain-y.

Related

Cannot find FakeInput

I am trying add a fake input to simple_form using the following suggestion (rails simple_form fields not related to the model), but it does not work for me.
This is my HAML code for adding a fake input field.
= f.input :agrees_to_terms, :as => :fake
I put the following class in app/inputs/fake_input.rb
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input
template.text_field_tag(attribute_name, nil, input_html_options)
end
end
I get the following runtime error
No input found for fake
I have two initializers that were generated by Rails Composer: simple_form.rb and simple_form_bootstrap.rb
How can I make Rails find my FakeInput class?
I had the same issue, and a server restart resolved it.

Troubles with Rails 3 and classes in nested directories

I have a rails project that I inherited. I'm working on upgrading it from rails 2 to rails 3. I'm at 3.0.15 right now, working my way up to 3.2. In the app directory there is a support folder with classes in it. Like so:
app/
support/
sweeper.rb
grouped_list/
item.rb
list.rb
item.rb looks like this:
class Support::GroupedList::Item
attr_accessor :name, :count, :link
def initialize(name, count, link)
#name = name
#count = count
#link = link
end
end
In one of the controllers the Items class is called like this:
Support::GroupedList::Item.new name, count, link
when I try and load a page I get the error:
uninitialized constant ApplicationController::Support
My first thought was that the paths where not in the autoload so I added
config.autoload_paths += %W(#{config.root}/app/support #{config.root}/app/support/grouped_list)
in my application.rb but that didn't change anything. After looking around for examples I found that people where creating empty module in their directory structure so I created a support.rb file in the support folder that looks like this:
module Support
end
Now my error changes to
uninitialized constant Support::GroupedList
Hey! That's interesting, I don't understand why it changed but its progress right? So I add a grouped_list.rb file in the grouped_list directory. It looks like this:
module Support::GroupedList
end
But then I get
Expected ***/app/support/grouped_list/grouped_list.rb to define GroupedList
Ugh! so I change it to:
module GroupedList
end
But then I'm back to:
uninitialized constant Support::GroupedList
So clearly there is some fundamental knowledge I'm missing about loading classes in modules. This structure worked in rails 2 but breaks in rails 3.0.15. I'd really like to understand so if anyone can help me out I'd really appreciate it.
class GroupedList::Item
attr_accessor :name, :count, :link
def initialize(name, count, link)
#name = name
#count = count
#link = link
end
end
This should work. Then in your controller:
GroupedList::Item.new name, count, link
Also remember to restart your rails server/console, after you make these changes.

How do you solve FixtureClassNotFound: No class attached to find

When running my tests I get this error:
FixtureClassNotFound: No class attached to find
What causes this error and how to fix it?
Most likely this happens because a custom table name is used for a Model (using the set_table_name) or the model is in a module.
To solve, you need to add a set_fixture_class line in the test_helper.rb before the fixtures :all line:
class ActiveSupport::TestCase
self.use_transactional_fixtures = true
.
.
.
set_fixture_class my_table_name: MyModule::MyClass
fixtures :all
end
In this case the fixtures file should be called my_table_name.yml
NOTE: It would be helpful if you included the stack trace and the full error message.
In your test/test_helper.rb class, there is a line like
fixtures :all
This tells the framework to look in the directory test/fixtures and try to load each of the YAML files that it sees there and then save them to the DB. So my hunch is that you have a file in there that does not have class in app/models with the singularized name. In other words, if there is a file test/fixtures/posts.yml, then when you run your tests, the framework will look for a class named Post to load your data in.
So the first thing I would do is check to see if you have a fixture file that is not associated with one of your model classes (maybe you deleted a model but forgot to delete the fixture?)
If that doesn't work, try changing the line in your test helper to explicitly load the fixtures you need. So if you only want to load fixtures for an object named Post and an object named User, you will change:
fixtures :all
to
fixtures :posts, :users
in test_helper.rb and you should see the error go away (although other errors may now appear because your fixtures are not loaded.0
In the Event that the Class is an irregular class in terms of naming such as fish, sms
it could have been created by using the --force-plural flag
i.e
rails g model sms --force-plural
in that case you would set up an inflection which is set up under
config/initializers/inflections.rb
an example of such is this
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.plural /^(ox)$/i, '\1en'
inflect.singular /^(ox)en/i, '\1'
inflect.irregular 'person', 'people'
inflect.uncountable %w( fish sheep )
end
In this way the class can be recognized as you declared it
I got this error when I made a mistake generating some scaffold code. I used a plural model name, and that just confused the fixture loading, I guess. Regenerating the scaffold with a singular model name fixed the problem.

Adding to Rails autoload_path from Gem

I want to write a gem that adds a app/services directory to a Rails application.
Since I want to add it from within the Gem i came up with this solution:
class Railtie < ::Rails::Railtie
config.after_initialize do |app|
::Rails.logger.info "adding #{ActiveService::Configuration.path} to autoload_path"
app.config.autoload_paths = [ActiveService::Configuration.path] + app.config.autoload_paths
end
end
The problem is that config.autoload_path is a frozen array, so that modifing it seems not to be a good idea.
Any suggestions of how this could be achieved in a better way?
config.autoload_paths is frozen inside the :set_autload_paths initializer. The Array is passed on to ActiveSupport::Dependencies.autoload_paths, so modifying the original Array would not make much sense. Therefore it's frozen.
You should be able to hook into :before => :set_autoload_paths and extend config.autoload_paths before it's passed and frozen:
class Railtie < ::Rails::Railtie
initializer 'activeservice.autoload', :before => :set_autoload_paths do |app|
app.config.autoload_paths << ActiveService::Configuration.path
end
end
Documentation about initializer hooks can be found at guides.rubyonrails.org/initialization.html
First, all directories under app/* are already in the load path since Rails 3.0. In any case, if you want to do it, you should use the paths api instead. Example from Rails source code:
https://github.com/rails/rails/blob/master/railties/lib/rails/engine/configuration.rb#L42

Rails 3 Custom Validator Problem

I need to apply validation to a Model so that 2 integer values in the record, minimum and maximum, form an inclusive range (ex. 2 and 3 are ok, but 4 and 1 are not). From what I understand, since I need to validate 2 values against each other in the same record, I have to use ActiveModel::Validator (and not ActiveModel::EachValidator). So, I try the following:
Model (app/models/position.rb):
class Position < ActiveRecord::Base
validates_with InclusiveRangeValidator
end
Validator (app/lib/validators/inclusive_range_validator.rb):
class InclusiveRangeValidator < ActiveModel::Validator
def validate(record)
record.errors[:base] << "#{record.minimum} and #{record.maximum} do not form a valid inclusive range." unless record.minimum < record.maximum
end
end
I've read that Rails 3.0.5 doesn't automatically load the lib directory anymore, so I added this line in config/application.rb:
config.autoload_path += %W({config.root}/lib)
And then I reset the rails server so it'll take the change to config/application.rb. I run my unit tests, which tries to exercise the model to prove this validation works. I get this error:
uninitialized constant: Position::InclusiveRangeValidator (NameError)
What I think is happening is that Rails is not recognizing/finding my custom validator class, and so it assumes InclusiveRangeValidator is a constant in the class that it's referenced in. But, I thought the change I made to config/application.rb would put my validator in the load path so that it would be available.
I've gone through the other posts on StackOverflow and didn't come up with a solution, and I've read the API docs on validators, to no avail. I've got to be doing something simple and stupid, but I can't see what the issue is. Any help?
EDIT:
After more searching, I discovered that I don't need a custom validator at all, as I can accomplish the same goal with this:
validates :minimum, :numericality => {:greater_than_or_equal_to => 0 }
validates :maximum, :numericality => {:greater_than => :minimum }
However, the question still remains as to why Rails can't locate the custom validation class.
Once, I changed the line in application.rb to:
config.autoload_paths += %W[#{config.root}/lib/validators/]
Rails was able to find the right path to load my custom validator. I made the mistake of assuming Rails would automatically recurse the directory structure, this is evidently not the case.