Troubles with Rails 3 and classes in nested directories - ruby-on-rails-3

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.

Related

Rails - include module into controller, to be used in the view

I'm really new to Rails and I try to setup a module file to be used in the view. So I believe the correct behavior is to define the module as a helper within a controller and voila, it should be working. However, that's not the case for me. Here is the structure.
lib
functions
-- form_manager.rb
form_manager.rb:
Module Functions
Module FormManager
def error_message() ...
end
end
end
users_controller.rb
class UsersController < ApplicationController
helper FormManager
def new ...
Well, the structure is like the above and when I call the error_message from new.html.erb it gives me the error: uninitialized constant UsersController::FormManager.
So, first of all, I know that in rails 3 lib is not automatically loaded. Assuming that it is not mandatory to autoload the lib folder, how can I make this work and what am I missing?
BTW, please don't say that this question is duplicate. I'm telling you I've been searching for this crap for almost 2 days.
Your module is not autoloaded (at least not in 3.2.6). You have to load it explicitly. You can achieve this with the following line of code
# in application.rb
config.autoload_paths += %W(#{config.root}/lib)
You can check your autoload paths with Rails.application.config.autoload_paths. Maybe it's indeed defined for you?
Now you're sure your module gets loaded, you can check it in rails console by calling
> Functions::FormHelper
Now you can't use that module as a view helper by default. Use #included to define the helper when your module gets included. You achieve "lazy evaluation" this way. I think the problem with your code is that the helper method gets called before the module gets included. (somebody should correct me if I'm wrong)
Here's the code:
Module Functions
Module FormManager
def error_message() ...
end
def self.included m
return unless m < ActionController::Base
m.helper_method :error_message
end
end
end
You should also remove the helper line from your controller.
EDIT:
You can achieve this without autoloading. Just use require "functions/form_manager". You define a helper_method for every method. If you wish use all the module methods as helpers use
def self.included m
return unless m < ActionController::Base
m.helper_method self.instance_methods
end
EDIT2:
It appears that you don't need to use self.included. This achieves the same functionality:
class ApplicationController < ActionController::Base
include Functions::FormManager
helper_method Functions::FormManager.instance_methods
end
It appears you are namespacing FormManager inside of Functions which means you would call it by helper Functions::FormManager
Try that

RoR 3.0.9 - Passing a variable from routes.rb to application_controller.rb or viceversa

I have a multi site project that changes from one to another by changing two variables, one is inside routes.rb and the other in application_controller.rb. Is it possible to pass variables between these files so that I only have to change a parameter to achieve the change required?
On my routes.rb file I use this variable to assign the correct controller the routes it should use. For example:
def showsite
"mysite1"
end
root :to => "#{showsite}#index"
And on application_controller.rb I use the same parameter to get the domain of the site, some layouts it should use and another things. For example:
before_filter :set_defaults
def showsite
"mysite1"
end
def set_defaults
if "#{showsite}" == "mysite1"
#domain = 'mysite1.com'
elsif "#{showsite}" == "mysite2"
#domain = 'mysite2.com'
else
#domain.nil?
end
end
def special_layout
"#{showsite}"
end
Every time I want to show a different version of the project I need to change two variables. I know its not a lot but I have to do it many times a day. Im pretty new on RoR, if there is a better solution please guide me to it. Thanks!
Why not use a Rails Initializer. E.g. the following:
File: `config/initializers/showsite.rb`
with the following content:
MyApp::Application.config.showsite = 'mysite1'
Then you should be able to use:
def showsite
MyApp::Application.config.showsite
end
and similar in your routes file.

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.

rails3-amf - to_amf method not found on ActiveRecord objects

I am using the rails3-amf gem by warhammerkid in my Rails 3 / Flex 4 project.
AFAIK, I have correctly followed the "Getting Started" instructions from the GitHub page.
I have added the gem lines to my Gemfile.
I have installed the gems using bundle install.
From my Flex application, I will be making the RemoteObject call to the index action in the ManageMySchool::GradesController file. This is the code in the app/controllers/manage_my_school/grades_controller.rb file:
class ManageMySchool::GradesController < ApplicationController
respond_to :html, :amf
def index
#grade = Grade.first
respond_with(#grade) do |format|
format.amf { render :amf => #grade.to_amf }
end
end
end
The name of the model which is to be serialized is called Grade in both the Rails project (app/models/Grade.rb) and the Flex project (Grade.as with a RemoteAlias set as Grade). In the config/application.rb file, I have done the class mapping this way:
config.rails3amf.class_mapping do |m|
m.map :as => 'Grade', :ruby => 'Grade'
end
And I have done a parameter mapping this way:
config.rails3amf.map_params :controller => 'ManageMySchool::GradesController', :action => 'index', :params => [:authenticity_token]
Problem
Now, when I run the server and make the RemoteObject call from Flex, I get a to_amf undefined method error for the Grade model.
If I change Grade.first to Grade.all, #grade would have an array of Grades. But the undefined method error message still mentions the Grade model. This means that the to_amf method is working for the Array class but not for the ActiveRecord model.
Why is this? What am I doing wrong?
Is there something I have to do to "enable" the rails3-amf gem for ActiveRecord models?
I would appreciate any insights. Thanks!
Update
#warhammerkid: Here is the output of Grade.ancestors as seen in rails console.
ree-1.8.7-2011.03 :006 > puts Grade.ancestors
Grade
ActiveRecord::Base
Paperclip::CallbackCompatability::Rails3::Running
Paperclip::CallbackCompatability::Rails3
Paperclip::Glue CanCan::ModelAdditions
Authlogic::ActsAsAuthentic::ValidationsScope
Authlogic::ActsAsAuthentic::SingleAccessToken
Authlogic::ActsAsAuthentic::SessionMaintenance
Authlogic::ActsAsAuthentic::RestfulAuthentication::InstanceMethods
Authlogic::ActsAsAuthentic::RestfulAuthentication
Authlogic::ActsAsAuthentic::PersistenceToken
Authlogic::ActsAsAuthentic::PerishableToken
Authlogic::ActsAsAuthentic::Password
Authlogic::ActsAsAuthentic::MagicColumns
Authlogic::ActsAsAuthentic::Login
Authlogic::ActsAsAuthentic::LoggedInStatus
Authlogic::ActsAsAuthentic::Email
Authlogic::ActsAsAuthentic::Base
ActiveRecord::Aggregations
ActiveRecord::Transactions
ActiveRecord::Reflection
ActiveRecord::Serialization
ActiveModel::Serializers::Xml
ActiveModel::Serializers::JSON
ActiveModel::Serialization
ActiveRecord::AutosaveAssociation
ActiveRecord::NestedAttributes
ActiveRecord::Associations
ActiveRecord::AssociationPreload
ActiveRecord::NamedScope
ActiveModel::Validations::Callbacks
ActiveRecord::Callbacks
ActiveModel::Observing
ActiveRecord::Timestamp
ActiveModel::MassAssignmentSecurity
ActiveRecord::AttributeMethods::Dirty
ActiveModel::Dirty
ActiveRecord::AttributeMethods::TimeZoneConversion
ActiveRecord::AttributeMethods::PrimaryKey
ActiveRecord::AttributeMethods::Read
ActiveRecord::AttributeMethods::Write
ActiveRecord::AttributeMethods::BeforeTypeCast
#<Module:0x1028356f0> ActiveRecord::AttributeMethods::Query
ActiveRecord::AttributeMethods
ActiveModel::AttributeMethods
ActiveRecord::Locking::Optimistic
ActiveRecord::Locking::Pessimistic
ActiveRecord::Validations
ActiveModel::Validations::HelperMethods
ActiveModel::Validations
ActiveSupport::Callbacks
ActiveModel::Conversion
ActiveRecord::Persistence Object
PP::ObjectMixin Base64::Deprecated
Base64
ActiveSupport::Dependencies::Loadable
Kernel
Note that only ActiveModel::Serialization is there. No mention of Rails3AMF.
Does this mean I have to do something special to load the Rails3AMF module for the ActiveRecord models?
I am using Rails 3.0.5 with the latest version of ree. The gems are all contained in a gemset managed using rvm.
Update 2
If I remove the to_amf in the render :amf line, then I get the following error:
Grade Load (0.3ms) SELECT `grades`.* FROM `grades` LIMIT 1
Completed 200 OK in 195ms (Views: 0.1ms | ActiveRecord: 0.8ms)
Sending back AMF
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.last):
Rendered /Users/anjan/.rvm/gems/ree-1.8.7-2011.03#rb/gems/actionpack-3.0.5/lib/> > action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
Rendered /Users/anjan/.rvm/gems/ree-1.8.7-2011.03#rb/gems/actionpack-3.0.5/lib/> > action_dispatch/middleware/templates/rescues/_request_and_response.erb (2.8ms)
Rendered /Users/anjan/.rvm/gems/ree-1.8.7-2011.03#rb/gems/actionpack-3.0.5/lib/> > action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (13.6ms)
Started POST "/amf" for 127.0.0.1 at Fri Apr 15 17:03:34 +0530 2011
Sending back AMF
Update 3
If I manually add the line include Rails3AMF::Serialization at the top of the Grade.rb model file, then it all works. So, does this mean that I have to put this line in all my models?
I see that this is already being done in line numbers 40 - 42 in the lib/rails3-amf/serialization.rb file of the gem:
# Hook into any object that includes ActiveModel::Serialization
module ActiveModel::Serialization
include Rails3AMF::Serialization
end
Why isn't this working? Should I force-load the gem when my application initializes or something?
Thanks!
Update 4 - Solved by this workaround
Okay, I just ended up adding this code block in an initializer:
class ActiveRecord::Base
include Rails3AMF::Serialization
end
And it is working.
#warhammerkid - Thanks for the help.
Rails3AMF::Serialization, the module that adds the to_amf method, is included in ActiveModel::Serialization when Rails3-AMF loads. If it's somehow not being included even though the code is running and ActiveModel::Serialization is one of your model's ancestors, then the simplest solution is just to add "include Rails3AMF::Serialization" at the top of your model implementation. I've never tried gem sets before, but it might be an issue with them, as everything works correctly using Bundler.
As an aside, feel free to post a bug to https://github.com/warhammerkid/rails3-amf/issues.

Rails 3 namespacing requires model to be defined twice?

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.