Archive for the ‘clean’ tag
Cleaning up the models directory
While most of the files in app/models certainly model something, there are also files that do not really belong in there, and if only so that classes with a similar structure or purpose are grouped together. Of course, I’m talking about mailers and observers here, but it could also be any kind of user-defined file types, like e.g. Liquid Drops.
So, this article is for those people, who are annoyed by their cluttered app/models directory, and possibly already use Rails Engines or some other application slicing framework (not a requirement for the following tips, but you can skip the end of this article, if you have a monolithic application).
The recipe
- The first thing you’d do is to create the directories
app/mailersandapp/observers, and, if you use Engines or the like, repeat that step for every app slice you have with mailers and/or observers. - Now for the fun part. Move all your observers and mailers into their respective directories.
- Possibly hours later (depending on the size of your code base), you should be at the point that the previous step is finished, but by now, most of your tests should fail. But don’t worry, we’ll fix that in a sec.
- In your
config/environment.rb, add your new directories you just created (hours ago) to the load paths. InsideRails::Initializer.run, enter the following code:
config.load_paths += [
:mailers,
bservers,
# Add any other file types that you might have identified
].collect { |path| "#{RAILS_ROOT}/app/#{path}" }
Again, if you use engines, you need to add another file. In config/initializers/engines.rb, paste the following code:
# Note that these are singular!
file_types = [
:mailer,
bserver,
]
Engines.mix_code_from *(file_types.collect(&:to_s))
Rails.plugins.each do |plugin|
file_types.each do |file_type|
Dependencies.load_paths << "#{plugin.directory}/app/#{file_type.to_s.pluralize}"
end
end
There is one caveat with the vanilla version of engines and mailers: For this code to work, your mailer files must end with _mailer.rb (which IMHO is a good convention anyways).
Getting observers to work with engines
Now for the engines / app slicer – only part (others can stop reading here). Your observers that are not in RAILS_ROOT/app/observers can not be found by Rails, if you list them in the Initializer in environment.rb. However, there is an easy way to fix that. Just add another custom initializer to declare your observers, and remove them from environment.rb:
config/initializers/observers.rb
ActiveRecord::Base.observers = [
:article_observer,
:user_observer,
# add more here
]
ActiveRecord::Base.instantiate_observers
By the time that this initializer gets executed, the engines initializer should already have run (remember that initializers are executed in alphabetical order, so if you name the engines initializer engines.rb and the observer initializer observer.rb, you should be fine.)
Popularity: 1% [?]
Better Rails Initializers
Recently, I ported our environment.rb to the new Rails Initializers. Over time, we added a lot of initialization code in environment.rb. In particular, there are a lot of small monkey-patches that we apply to rails, or 3rd-party plugins. So for that, the Rails Initializers are a very good fit.
Still, after I cleaned up that part, there were still things like this in environment.rb:
google_maps_api_key = case RAILS_ENV
when 'test' then "the-test-key"
when 'development' then "the-development-key"
when 'production' then "the-production-key"
end
GeoKit::Geocoders.google = google_maps_api_key
While this could arguably be handled by a YAML configuration file, unfortunately GeoKit (at least the version we use right now) does not support configuration via YAML.
Another thing we do is disable Globalize in the development environment to speed up page rendering on our local machines. So the corresponding part of the environment.rb file looked something like
if RAILS_ENV == 'production'
include Globalize
else
# In reality, this is slightly bigger, to simulate more methods
# of the Globalize API
class String; def t; self; end; end
end
Hardcoding environment conditionals is somewhat like hardcoding admin privileges to the user with ID 1. Rather, an environment should be seen as a generic container for a bunch of configuration options. So I came up with a better way of using Rails Initializers like follows:
By default, Rails loads every Ruby file in config/initializers and its subdirectories after the plugins are fully loaded. This is handlet by the Rails::Initializer#load_application_initializers method. I wrote a small monkey patch to change this behavior such that only the files in config/initializers (without subdirectories) are loaded, followed by the files in config/initializers/RAILS_ENV.
class Rails::Initializer
def load_application_initializers
["#{configuration.root_path}/config/initializers/*.rb",
"#{configuration.root_path}/config/initializers/#{RAILS_ENV}/**/*.rb"].each do |path|
Dir[path].sort.each do |initializer|
load(initializer)
end
end
end
end
With this code, our Globalize initialization is distributed across two files:
config/initializers/globalize.rb
class String
def t
self
end
end
config/initializers/production/globalize.rb
include Globalize
Note: In recent Rails versions, the original load_application_initializers method is slightly more complex, such that the following patch would be used instead (warning: I have not tried that one just yet, since we’re still porting to Rails 2.1)
class Rails::Initializer
def load_application_initializers
return unless gems_dependencies_loaded
["#{configuration.root_path}/config/initializers/*.rb",
"#{configuration.root_path}/config/initializers/#{RAILS_ENV}/**/*.rb"].each do |path|
Dir[path].sort.each do |initializer|
load(initializer)
end
end
end
end
Note that this monkey patch can not be applied as a Rails Initializer, since it patches the way Rails Initializers are loaded (though it would be awesome, if something like this would be possible; effectively patching a method while it is executed
). This means that you need to require the file containing the monkey patch from environment.rb, before you run the Rails Initializer.
Enjoy your cleaned up environment.rb and initializers.
Popularity: 1% [?]
