Rails application settings and tests - ruby-on-rails

In my app I've used SettingsLogic to handle the app's settings (such as facebook tokens etc.) which is a gem that basically parses the config/application.yml file and provides easy access to its content.
I've also used this configuration file to enable or disable i18n support for the entire app, as the app is more of a core app for many child apps.
And so in my routes.rb I do things like :
if Settings.i18n.enabled
match ':this', :to => 'that#place'
end
Or in models :
if Settings.i18n.enabled
scope :for_current_locale, lambda { where(:locale => I18n.locale) }
end
I'd like to test how the app responds to both states : when i18n is turned off and when it is turned on.
My problem is that the state is read from the configuration file when Rails initializes. So when I run my tests I could only run tests related to i18n.enabled being false, then change my configuration file and run tests related to i18n.enabled being true.
Is there some way I could reinitialize the app between 2 tests ? (I'm using Rspec)
Or should I automate some way of running 2 separate tests files for both i18n cases ?
Or maybe there is a better way ?
Thanks !
EDIT
As for the routes issue I managed to make the specs pass by changing my settings and reloading the routes explicitly :
before(:all) do
Settings.i18n["enabled"] = true
My::Application.reload_routes!
end
But still I feel this is not ideal, and what about things defined in models ? Can I reload my models as well before running specs ? Won't that duplicate stuff like callbacks ?

I think you simple can reconfigure your Application like following, for example in a rspec before_all block in your specific test file:
YourAppName::Application.configure do
config.i18n.enabled = true # or false
end
And in an after_all block you can set the configuration back to your default one.
I think that could work.

Related

Where is a logical place to put a file flag?

In a Ruby on Rails application, where would the most logical place be to put a "file flag."
I am attempting to externalize configuration and allow the presence of a file to be the deciding factor on whether or not something shows on the webapp.
Right now, I have a file here:
lib/
deployment_enabled
Model deployment.rb
class Deployment...
...
def deployment_enabled?
Dir["#{Rails.root}/lib/deployment_enabled"].any?
end
end
Now this works of course, but i'm not sure this follows the MVC paradigms, since the lib directory should consist of scripts. I could put it in config, but again - not sure it belongs there as rails uses this for rails specific configuration, not necessarily the webapp.
I could of course put this in our database, but that require a new table to be created, and that seems unnecessary.
Where's the most logical place to put this flag file? Does Rails have a directory that's created during the generation to put these sort of files?
I suggest using the Rails tmp directory for this purpose. Then:
File.exist?("#{Rails.root}/tmp/deployment_enabled")
Phusion Passenger use this kind of mechanism too.
https://www.phusionpassenger.com/library/walkthroughs/basics/ruby/reloading_code.html#tmp-always_restart-txt
I recommend that you follow the Twelve-Factor App guidelines and keep your code separate from your configuration. In this case you are really talking about a simple boolean value, and the presence of the file is just the mechanism you use to define the value. This should be done instead through an environment variable.
Something like:
DEPLOYMENT_ENABLED=1 RAILS_ENV=production rails server
You would then use an initializer in Rails to detect the value:
# config/initializers/deployment.rb
foo if ENV['DEPLOYMENT_ENABLED']
The value can still be modified at runtime, e.g., ENV['DEPLOYMENT_ENABLED'] = 0.

Can I run a Rails engine's specs from a real app which mounts it?

I have a Rails Engine meant to provide some models and controllers to a larger project of ours. There's a pretty decent set of specs for the Engine, using a bunch of mocks and some full-scale models and controllers within the engine's dummy app to make sure the Engine is doing what it's supposed to and working with the larger application.
However, even with all tests passing, I frequently find broken behavior when I update the engine in the larger application. If my tests are passing but the behavior is broken, clearly something's wrong with the tests, but what? Am I mocking too much, or not enough?
To get me closer to figuring this out, I'd like to be able to run the engine's tests from inside the full application. This seems like it should be possible, but I don't understand rspec thoroughly enough to get a handle on how.
(This is related to this question but not quite the same; I'm not trying to run all the specs from one command, just to run the engine's specs within the full app environment. This also seems to be related. Actually, I've read every question tagged with both rspec and rails-engines--there aren't many--and they're all either not what I need, or unanswered.)
The simplest solution would be to specify the paths in rspec command. If you have directory structure
/project
/engine
/engine_2
Then you do and should run all the specs
cd project
rspec spec/ ../engine/spec ../engine_2/spec
But if you want to run specs on Continous Integration or just this doesn't seem to be comfortable I solved this problem with a customized rake spec task, changing the pattern method.
lib/task/rspec.rake should look like this
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
RSpec::Core::RakeTask.module_eval do
def pattern
extras = []
Rails.application.config.rspec_paths.each do |dir|
if File.directory?( dir )
extras << File.join( dir, 'spec', '**', '*_spec.rb' ).to_s
end
end
[#pattern] | extras
end
end
In engine class you add a path to config.rspec_paths
class Engine < ::Rails::Engine
# Register path to rspec
config.rspec_paths << self.root
end
And don't forget to initialize config.rspec_paths somewhere in a base project.
If you want to add factories then you can create initializer, you can find solution somewhere here on stackoverflow.
Not sure if this solution is the best but works for me and I am happy with that. Good luck!

sandbox testing environments exist for TDD'ing command line application

I'm writing a command line command but want to TDD it. I'll be creating and deleting files and was wondering if there's a sandbox testing gem or something like that. I'm using ruby and rspec.
Depends on what you're trying to do, but I test most of my command line Ruby by mocking out the file system and STDIN/STDOUT. Using dependency injection I often end up with something along these lines:
describe Add do
it 'writes the result to standard out' do
console = mock('STDOUT')
console.should_receive(:puts).with('5')
Add.new(console).execute(3,2)
end
end
class Add
def initialize(out = STDOUT)
#out = out
end
def execute(command_line_args)
#out.puts(command_line_args.inject(:+))
end
end
Add.new.execute(ARGS)
By using default values I can inject in the test, but leave it out of the production code.
Hope that helps!
Brandon
The template generated by the newgem install_cucumber generator uses a pattern that I like quite a bit. Have a look at the support/env.rb and support/common.rb files it creates:
https://github.com/drnic/newgem/blob/master/rubygems_generators/install_cucumber/templates/features/support/env.rb.erb
https://github.com/drnic/newgem/blob/master/rubygems_generators/install_cucumber/templates/features/support/common.rb
Use of it in test looks like this:
in_tmp_folder do
# The current directory is now a generated tmp folder.
# If you stick to relative paths, everything you do in here should be safe
end
The files linked to above are for using this in cucumber tests, but it could easily be adapter to whatever framework you're using. The env.rb above deletes the tmp folder before each test starts.
You might also want to take a look at the sandbox gem.
gem install sandbox
Example usage is here: https://github.com/bdimcheff/sandbox

How can I keep my initializer configuration from being lost in development mode?

I'm working on a Rails app that uses an engine. I'm using an initializer to configure one of my engine's controllers so that it will trigger an action in the host app. The code looks something like this:
# config/initializers/my_engine.rb
MyEngine::SomeController.after_filter proc {
# Do something in the host app
}, :only => :update
This works fine in production, but in development mode, the proc is only called on the first request. This is because the classes are getting reloaded and this configuration is lost, because it was stored in a class variable. (For example, MyEngine::SomeController is reloaded from the file it's in, and since the after_filter isn't declared there, it isn't added back on.)
Some Rails background
In development mode, Rails uses the following load strategy:
Code in the app directory is reloaded on each request, on the assumption that you're actively changing it.
Code in the lib directory, along with config/initializer files, are loaded once, when the application boots.
Initializer files are generally used for configuring gems. In the past, gems have mostly had code in the lib directory, so running their configuration once was sufficient.
How engines change things
However, Rails engines have code in the app directory: controllers, models, etc. These files are reloaded in development mode on each request. Therefore, configuration like my example above is lost.
Enter to_prepare
Rails provides config.to_prepare specifically to solve this problem: it run once in production, and on every request in development.
For example, we have this in application.rb, which works fine:
config.to_prepare do
# set up class variables (after_filters, etc)
end
However, if I have to put all my engines' configuration in application.rb, this defeats the point of config/initializers in keeping things organized.
So, for any configuration of classes in my engines' app directories, I want to put that code in files under config/initializers.
Here are my questions.
I'm unclear how to get config into scope in an initializer file. I'm thinking it would be Rails.application.config. Is that right?
Can I add add multiple to_prepare blocks? I'm afraid that calling it multiple times will overwrite previous blocks.
Update
As #Frederick Cheung mentioned, Rails.application.config.to_prepare does work in config/initializer files, and one can use as many of these as needed in the various files; each call appends its block to an array, so nothing is overwritten.
So the solution to this problem is:
# config/initializers/my_engine.rb
Rails.application.config.to_prepare do
MyEngine::SomeController.after_filter proc {
# Do something in the host app
}, :only => :update
end
One thing that still seems odd: I expected the to_prepare block to be called on every request in development mode, but instead it seems to be called randomly every 3rd request or so. I added block:
Rails.application.config.to_prepare do
Rails.logger.info "Running the prepare block!"
end
... restarted my app, and refreshed the page nine times. I only saw the message on the 1st, 5th, 7th and 9th requests. I'm not sure what explains this behavior, but it does explain why my code without the to_prepare worked intermittently in development.
You can add as many to_prepare blocks as you want - when you do config.to_prepare, Rails is doing (in configuration.rb in railties)
def to_prepare(&blk)
to_prepare_blocks << blk if blk
end
and then iterates over those blocks handing them over to ActionDispatch::Reloader, where to_prepare is implemented using ActiveSupport::Callbacks (i.e. the same thing that is used for before_save and so on). Multiple to_prepare blocks are fine.
Currently it looks like Rails iterates over to_prepare_blocks after reading application initialisers so adding to Rails.application.configuration.to_prepare should work. You may prefer to use ActionDispatch::Reloader.to_prepare.
There's nothing to stop you from doing initializer code in a file that lives in app/models.
for example
class MyClass
def self.run_me_when_the_class_is_loaded
end
end
MyClass.run_me_when_the_class_is_loaded
MyClass.run_me... will run when the class is loaded .... which is what we want, right?
Not sure if its the Rails way.... but its extremely straightforward, and does not depend on the shifting winds of Rails.

Rails: filter defined in lib file required in environment.rb disappears from filter_chain in production environment. Why?

In my rails application, I have a file in lib that, among other things, sets up a filter that runs on all controllers.
When running under development environment, everything runs fine. However, under production the filter goes missing. Funny thing is, by inspecting the filter_chain, I noticed other filters remain, eg. those defined in plugins, or later in the specific controller class.
I've tested this with both rails edge and v2.3.0.
Testing update:
I've now tested with older rails and found the issue to be present back to v2.1.0, but not in v2.0.5, I've bisect them and found the 986aec5 rails commit to be guilty.
I've isolated the behavior to the following tiny test case:
# app/controllers/foo_controller.rb
class FooController < ApplicationController
def index
render :text => 'not filtered'
end
end
# lib/foobar.rb
ActionController::Base.class_eval do
before_filter :foobar
def foobar
render :text => 'hi from foobar filter'
end
end
# config/environment.rb (at end of file)
require 'foobar'
Here's the output I get when running under the development environment:
$ script/server &
$ curl localhost:3000/foo
> hi from foobar filter
And here's the output for the production environment:
$ script/server -e production &
$ curl localhost:3000/foo
> not filtered
As alluded to before, it works fine for any environment when I do the same thing via plugin. All I need is to put what's under lib/foobar.rb in the plugin's init.rb file.
So in a way I already have a workaround, but I'd like to understand what's going on and what's causing the filter to go missing when in production.
I conjecture it's something in the different ways Rails handles loading in the different environments, but I need to dig deeper.
update
Indeed, I've now narrowed it down to the following config line:
config.cache_classes = false
If, in production.rb, config.cache_classes is changed from true to false, the test application works properly.
I still wonder why class reloading is causing such thing.
Frederick Cheung on the Rails list had the answer:
Because cache_classes does a little more than just that. The way
before filters work, if Foo < Bar then only those filters defined in
Bar at that point will be inherited by Foo - adding them to Bar at a
later date will not do anythingn
In development mode, the app starts, your file is required, the filter
added to ActionController::Base. Later the first request comes along,
the controller is loaded and it inherits that filter.
When cache_classes is true then all of your application classes are
loaded ahead of time. This happens before your file is required, so
all of your controllers already exist when that file is run and so it
has no effect. You could solve this by requiring this file from an
initializer (ensuring it runs before app classes are loaded), but
really why wouldn;t you just put this in application.rb ?
Fred
My real case was actually way more involved, and this is the way I found to solve the issue:
config.after_initialize do
require 'foobar'
end
The after_initialize block runs after the framework has been initialized but before it loads the application files, hence, it'll affect ActionPack::Base after it's been loaded, but before the application controllers are.
I guess that's the generally safe way to deal with all the preloading that goes on in production.
Drop a ruby script in
RAILS_ROOT\config\initializers
that contains
require "foobar.rb"
This invokes the before_filter for me.

Resources