Accessing models and modules in a Rails 7 initializer - ruby-on-rails

This issue relates to a need to set a Rails config variable as the application boots, and the value of that variable needs to come from data in the database (which are then modified). So, I have an initializer with something like this:
require "#{Rails.root}/lib/modules/facet_altering.rb"
include FacetAltering
Rails.application.config.reject_subjects = FacetAltering.reject
The reject method is potentially slow and calls the Subject model (which includes some concerns).
If I try to require subject.rb, application_rb and the relevant concerns from app/models then I progress a bit further, but eventually get stuck on uninitialized constant Subject::MySpecialConcern.
There might be some better way to set the reject_subjects value; I'd prefer not to run FacetAltering.reject each time the value of reject_subjects is used, though this might be an easy 'fix' if no other solution arises (at the cost of slowing things down). Or, is there another way to access these classes as the application boots?
Edit: Following on from the comment below, this is in config/application.rb:
%W[#{Rails.root}/lib/modules #{Rails.root}/test/mailers/previews].each do |path|
config.eager_load_paths << path
end

This post offered a useful clue:
Rails Model no longer available in initializer after upgrade to Rails 7.0
So, putting my code in config/application.rb as follows did the trick:
config.after_initialize do
Rails.application.config.reject_subjects = FacetAltering.reject
end
Now to find the answer to RuntimeError: Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations. and I might be able to complete this Rails 6 -> 7 upgrade!

Related

Losing namespace information in a rails namespaced model

When you create a namespaced model with rails scaffolding, you get two files. For example, this scaffold:
rails generate model Staff::Location name:string address:string
Generates these files:
/app/models/staff.rb
module Staff
def self.table_name_prefix
"staff_"
end
...
/app/models/staff/location.rb
class Staff::Location < ActiveRecord::Base
...
I am running into problems when in development mode where rails unloads the Staff module and never reloads it. This causes several annoying bugs such as Location not able to access it's table due to the missing table_name_prefix. The problem seems to crop up when I don't access the models directly, such as through a polymorphic relationship.
I can't seem to get the module loaded on a consistent basis. Is this the best practice way to do namespaced models? If it is, what am I missing?
Although I wasn't able to reproduce the problem in Rails 3.2.2, I've run into something like this before. The generic way to hack around this problem in development mode is through an ActionDispatch callback. Add this to config/environments/development.rb:
MyApp::Application.configure do
ActionDispatch::Callbacks.before do
load Rails.root.join('app', 'models', 'staff.rb')
end
end
Anything you do in that block will be executed before each request, so make sure you're only doing it in development mode.† Otherwise, you're going to suffer a performance hit in production.
I logged a message inside the staff.rb file and within the Staff module itself, and both messages appeared in the log for each request.
† I tried using the to_prepare callback, since that seems to be the documented way to execute code before each request only when cache_classes is false. But that only seemed to execute after restarting the application. There's at least one other open Stack Overflow question regarding this, although he's using a slightly different syntax than I used. If you can get to_prepare to work, I'd suggest that instead of before.
About a year later, I have finally found the answer to this question. This answer is specifically for rails 3.1. I am not sure if it is a problem in rails 3.2.
The problem occurs when setting up a model. If scaffolding is used, no helper file is generated. This would normally be in /app/helpers/staff/location_helper.rb. There are two ways to setup this file:
module Staff::LocationHelper
...
end
module Staff
module LocationHelper
...
end
end
In rails 3.1, specifically for helpers, you must use the first solution. You do not have to use it for other modules that use a namespace in other parts of the rails project. In fact, some structures in ruby require the second solution.
If you use the second solution when declaring a helper, in certain cases the Staff module in the helper file will override the module in /app/models/staff.rb. It will silently replace it with the empty Staff module in the file. This does not happen 100% of the time because helpers are not always loaded.

Explain to me how config works in Rails

I have a Rails 3 application, call it "MyApp". In my config\environments\production.rb file I see such things as
MyApp::Application.configure do
config.log_level = :info
config.logger = Logger.new(config.paths.log.first, 'daily')
...or...
config.logger = Logger.new(Rails.root.join("log",Rails.env + ".log"),3,20*1024*1024)
So, questions are focusing on terminology and wtf they mean... (or point me to some site ,I have looked but not found, to explain how this works.)
MyApp is a module?
MyApp::Application is a ...? What, a module too?
MyApp::Application.configure is a method?
config is a variable? How do I see it in console?
config.logger is a ???
config.paths.log.first is a ...??
--in console I can see "MyApp::Application.configure.config.paths.log.first" but don't know what that means or how to extract info from it!?!
Is this too much for one question? :)
I have looked at the tutorial http://guides.rubyonrails.org/configuring.html but it jumps right into what things do.
A six sided question! Oh my. Let's ahem roll.1 Here's hoping I receive 6 times the upvotes for it then? :)
1. MyApp is a module?
Yes, it's a module. It acts as a "container" for all things pertaining to your application. For instance you could define a class like this:
module MyApp
class MyFunClass
def my_fun_method
end
end
end
Then if someone else has a MyFunClass, it won't interfere with your MyFunClass. It's just a nice way of separating out the code.
2. MyApp::Application is a ...? What, a module too?
MyApp::Application is actually a class, which inherits from Rails::Application. This does a quite a lot of things, including setting up the Rails.application object which is actually an instance of MyApp::Application that you can do all sorts of fun things on like making requests to your application (in a rails console or rails c session). This code for instance would make a dummy request to the root path of your application, returning a 3-sized Array which is just a plain Rack response:
Rails.application.call(Rack::MockRequest.env_for("/"))
You can also get the routes for your application by calling this:
Rails.application.routes
The main purpose of defining MyApp::Application is not these fun things that you'll probably never use, but rather so that you can define application-specific configuration inside config/application.rb. Things like what parameters are filtered, the time zone of the application or what directories should be autoloaded. These are all covered in the Configuration Guide for Rails.
3. MyApp::Application.configure is a method?
Indeed it is a method, and it allows you to add further configuration options to your application's configuration after config/application.rb has been loaded. You've probably seen this used in config/environments/development.rb or one of the other two files in that directory, but basically they all use the same options as shown in that Configuration Guide linked to earlier.
4. config is a variable? How do I see it in console?
The config "variable" is actually a method defined within the code used for Rails::Application and returns quite simply a configuration object which stores the configuration for the application.
To access it in the console, just use Rails.application.config. This will return quite a large Rails::Application::Configuration object for your viewing pleasure.
5. config.logger is a ???
The method you're referring to, I assume, comes from this line in config/environments/production.rb:
# Use a different logger for distributed setups
# config.logger = SyslogLogger.new
The method in this example is not config.logger, but rather config.logger=, which is referred to as a "setter" method in Ruby-land. The one without the equal sign is referred to as a "getter". This method sets up an alternative logger for the production environment in Rails, which then can be accessed by using Rails.logger within the console or the application itself.
This is useful if you want to output something to the logs, as you can simply call this code:
Rails.logger.info("DEBUG INFO GOES HERE")
6. config.paths.log.first is a ...?? --in console I can see "MyApp::Application.configure.config.paths.log.first" but don't know what that means or how to extract info from it!?!
Within a Rails application, you can modify the locations of certain directories. And so, this config.paths method is a way of keeping track of where these directories map to. In my entire Rails life I have never had to use or modify this variable and that can mean either one of two things:
It's not used often by Rails programmers, or;
I don't live a very varied life.
Interpret it as you will. My main point is that you're probably never going to use it either.
I hope these help you understand Rails a little more!
1 Terrible dice joke.
MyApp is a module, it's a namespace including an app you'll launch, see next line
MyApp::Application is a Class and you're running it's instances when running a Rails app
MyApp::Application.configure is a method. It passes all instructions to the class. See Ref.
config is a method or an instance variable (when set) which belongs through inheritance to Rails::Application::Configuration. See Ref.
You can see it in console doing: MyApp::Application.config
config.logger doesn't exist until you define it, so it's a Logger instance. See Ref.
config.paths.log is a Rails::Paths::Path
you can access it in console using: MyApp::Application.config.paths.log

Ruby on Rails: Learning ActionController class - Question on $:.unshift activesupport_path and autoload method

Inside ActionController class (rails/actionpack/lib/action_controller.lib) I found several weird code. I don't really have a mentor to learn Ruby on Rails from, so this forum is my only hope:
Question #1: Could anyone help me explain these lines of codes?
begin
require 'active_support'
rescue LoadError
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
if File.directory?(activesupport_path)
$:.unshift activesupport_path
require 'active_support'
end
end
Especially the line with $:.unshift activesupport_path
In my thought, it tries to require active_support class, and if that doesn't work, it looks if activesupport_path is a directory, if it is, then . . . I totally lost it.
Question #2: What autoload method is for?
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/cookies'
.
.
.
Question #3: If I later find a method I don't understand what for, how is the best way to find out? As for that autoload method case, I tried to find it across my project (I have my Rails code frozen there) but couldn't find any clue. I searched for "def autoload". Am I doing things wrong? Is my IDE, TextMate just doesn't cut it?
Thank you!
In order for a file to be required you have to ensure that the path to it is in the Ruby $LOAD_PATH variable. This is has a short-hand version $: for legacy reasons, inheriting this from Perl.
When you call require, the interpreter looks for a .rb file in each of the paths given there until it finds a match. If it finds one, it is loaded. If not you get an exception.
Often you will see lines like this in files:
# script/something
# This appends "script/../lib" to the $LOAD_PATH, but this expands to
# something like "/home/user/project/lib" depending on the details of
# your installation.
$: << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))
You can use standard Array modifiers on $LOAD_PATH like unshift, push, and <<.
The first block of code is attempting to load active_support and only if that fails does it go about modifying the $LOAD_PATH to include the likely location of this file based on the path to the file making the require call. They do this because typically all gems from the Rails bundle are installed in the same base directory.
The reason for using unshift is to put that path at the highest priority, inserted at the front of the list. The << or push method adds to the end, lowest priority.
When you require a file it is loaded in, parsed, and evaluated, an operation which can take a small but measurable amount of time and will consume more memory to hold any class or method definitions inside the file, as well as any data such as string constants that may be declared. Loading in every single element of a library like ActiveRecord using require will require a considerable amount of memory, and this will import every database driver available, not just the ones that are actually used.
Ruby allows you to declare a class and a path to the file where it is defined, but with the advantage of not actually loading it in at that moment. This means that references to that class don't cause script errors in other parts of your application that make use of them.
You will often see declarations like this:
class Foo
# Declare the class Foo::Bar to be defined in foo/bar.rb
autoload(:Bar, 'foo/bar')
end
When using autoload you need to keep in mind that the class name is always defined within the scope of the module or class declaring it. In this example Bar is within Foo, or Foo::Bar using Ruby naming conventions.
When you make use of the Bar class, the foo/bar.rb file will be required. Think of it as creating a stub Bar class that transforms into the real class once it's actually exercised.
This is a great way of keeping a lot of options open, with many different modules ready to use, but without having to load everything into memory up front.
As for the third question, searchable documentation like APIDock will help you try and find more information on methods. The distinction between Ruby and Rails is often blurred, so you may have to check through both to be sure. Rails adds a lot of methods to core Ruby classes, so don't take the listing of methods available to be complete on either side. They work in conjunction.
Sometimes it pays to search for def methodname when trying to find out about where methodname originates, although this covers only conventional declarations. That method may be an alias from a mechanism like method_alias or may have been dynamically created using define_method, you can never really be sure until you dig around. At least 90% of the methods in Rails are declared the conventional way, though, so most of the time a simple search will yield what you want.

Exclude Sub Directory on Class Eager Load

I am facing an issue where my Rails application is set to cache classes when run in the staging or production environment. While load_paths only contains 'app/models', it appears that the initialization steps recursively caches everything in 'app/models'.
# Eager load application classes
def load_application_classes
if configuration.cache_classes
configuration.eager_load_paths.each do |load_path|
matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
require_dependency file.sub(matcher, '\1')
end
end
end
end
The problem in this is that we have a sub directory within 'app/models' that contains files with require statements that reference a concurrent JRuby environment. Since Rails knows nothing of this environment our application breaks on load.
As it stands, here are the proposed solutions...unfortunately only #1 is ideal.
1) The simplest solution would be to exclude the culprit sub directory, but have not found anything how to accomplish this.
2) Monkey patch the load_application_classes method to skip the specific sub directory.
3) Move the sub directory out from under 'app/models'. Feels a bit hackish and would require quite a few code changes.
Thoughts?
As a temporary measure you could go with a version of option 2 and override the definition of load_application_classes, replacing it with an empty implementation. That would force you to explicitly require the classes you need but it would give you complete control over what gets loaded and would be a completely transparent solution.
It sounds like your application is sufficiently sophisticated that it's growing beyond the Rails framework. I know that this doesn't directly answer your question so appologies in advance but you may want to consider looking at an alternative Ruby framework like Merb. Rails is great but sooner or later you bump into edge of the framework - sounds like that's where you are now.
We made the switch to Merb last year and haven't regreated it.
Chris

How to fix / debug 'expected x.rb to define X.rb' in Rails

I have seen this problem arise in many different circumstances and would like to get the best practices for fixing / debugging it on StackOverflow.
To use a real world example this occurred to me this morning:
expected announcement.rb to define Announcement
The class worked fine in development, testing and from a production console, but failed from in a production Mongrel. Here's the class:
class Announcement < ActiveRecord::Base
has_attachment :content_type => 'audio/mp3', :storage => :s3
end
The issue I would like addressed in the answers is not so much solving this specific problem, but how to properly debug to get Rails to give you a meaningful error as expected x.rb to define X.rb' is often a red herring...
Edit (3 great responses so far, each w/ a partial solution)
Debugging:
From Joe Van Dyk: Try accessing the model via a console on the environment / instance that is causing the error (in the case above: script/console production then type in 'Announcement'.
From Otto: Try setting a minimal plugin set via an initializer, eg: config.plugins = [ :exception_notification, :ssl_requirement, :all ] then re-enable one at a time.
Specific causes:
From Ian Terrell: if you're using attachment_fu make sure you have the correct image processor installed. attachment_fu will require it even if you aren't attaching an image.
From Otto: make sure you didn't name a model that conflicts with a built-in Rails class, eg: Request.
From Josh Lewis: make sure you don't have duplicated class or module names somewhere in your application (or Gem list).
That is a tricky one.
What generally works for me is to run "script/console production" on the production server, and type in:
Announcement
That will usually give you a better error message. But you said you already tried that?
I just ran into this error as well.
The short of it was that my rb file in my lib folder was not in a folder structure to match my module naming convention. This caused the ActiveSupport auto loader to use the wrong module to see if my class constant was defined.
Specifically I had defined the following class
module Foo
class Bar
end
end
In the root of /lib/bar.rb
This caused the autoloader to ask module Object if Bar was defined instead of module Foo.
Moving my rb file to /lib/foo/bar.rb fixed this problem.
I've encountered this before, and the AttachmentFu plugin was to blame. I believe in my case it was due to AttachmentFu expecting a different image processor than what was available, or non-supported versions were also installed. The problem was solved when I explicitly added :with => :rmagick (or similar -- I was using RMagick) to the has_attachment method call even for non-image attachments. Obviously, make sure that your production environment has all the right gems (or freeze them into your application) and supporting software (ImageMagick) installed. YMMV.
As for not getting Rails and AttachmentFu to suck up and hide the real error -- we fixed it before figuring it out completely.
Since this is still the top Google result, I thought I'd share what fixed the problem for me:
I had a module in the lib folder with the exact same name as my application. So, I had a conflict in module names, but I also had a conflict of folder names (not sure if the latter actually makes a difference though).
So, for the OP, make sure you don't have duplicated class or module names somewhere in your application (or Gem list).
For me, the cause was a circular dependency in my class definitions, and the problem only showed up using autotest in Rails. In my case, I didn't need the circular dependency, so I simply removed it.
You can try disabling all your plugins and add them back in one by one.
In environment.rb in the Initalizer section, add a line like this one:
config.plugins = [ :exception_notification, :ssl_requirement, :all ]
Start with the minimum set to run your application and add them in one by one. I usually get this error when I've defined a model that happens to map to an existing filename. For example, a Request model but Rails already has a request.rb that gets loaded first.
I had this problem for a while and in my case the error was always preceded from this S3 error:
(AWS::S3::Operation Aborted) "A
conflicting conditional operation is
currently in progress against this
resource. Please try again."
This problem usually occurs when creating the same bucket over and over again. (Source AWS Developers forum)
This was due to the fact that I had used attachment_fu to create the bucket and I had decommented the line containing the command Bucket.create(##bucket_name) in lib/technoweenie/attachment_fu/backends/s3_backends.rb (near to line 152).
Once commented or deleted the command Bucket.create(##bucket_name) the problem disappeared.
I hope this helps.
Changing class names while using STI caused this for me:
Class changed from 'EDBeneficiary' to 'EdBeneficiary'
Existing records had 'EDBeneficiary' stored in the 'type' column, so when Rails tried to load them up the exception was raised.
Fix: Run a migration to update values in the 'type' column to match the new class name.
in my case, I am getting this error in the development console but I can load the class in irb
Sorry this isn't a definitive answer, but another approach that might work in some specific circumstance:
I just ran in to this problem while debugging a site using Ruby 1.8.7 and Merb 1.0.15. It seemed that the class in question (let's call it SomeClass) was falling out of scope, but when some_class.rb file was automatically loaded, the other files it required (some_class/base.rb etc) were not loaded by the require mechanism. Possibly a bug in require?
If I required some_class file earlier, such as the end of environment.rb, it seems to prevent the object falling out of scope.
I was getting this error duo to a controller definition being in a file that wasn't named as a controller. For instance, you have a Comment model and you define the controller in a comment.rb file instead of comments_controller.rb
I had this problem with rails version 1.2.3. I could reproduce the problem only with mongrel, using console environment access didn't give any useful info. In my case, I solved making the RAILS_ROOT/html folder writable by mongrel and then restarting the web server, as some users reported here:
http://www.ruby-forum.com/topic/77708
When I upgraded rails from 1.1.6 to 1.2.6 and 2.0.5 for my app, I faced this error. In short, old plugins caused this error. These plugins were already out-dated and no update anymore (even no repo!). After I removed them, the app worked on 1.2.6 and 2.0.5. But I didn't check the detail source code of the plugins.

Resources