Ruby on Rails - Reload Class Cache on Demand - ruby-on-rails

Does Ruby on Rails 3 (3.0.7) offer a mechanism to reload the class cache on demand? I am running some tests where I overwrite some methods in ApplicationController and in some of my models to stub out authentication. For example, I call a method (I'm using Cucumber for testing) in some of my tags (Before('#tag')) that does this:
ApplicationController.send(:define_method, :current_user) do
#current_user ||= User.where(:id => cookies[:user_id]).first
end
...where the actual current_user method in ApplicationController has a different behavior.
This works great until I need to run some tests in which I do not want to stub out those methods. With config.cache_classes = true set in my test environment, ApplicationController is not reinitialized without my monkey patch, making the tests I don't want to stub out fail. With config.cache_classes = false, my monkey patch is forgotten on the next request, causing all of the tests that I need to stub to fail.
Ideally, I would like to be able to erase the class cache in an After('#tag') method, if this is possible. How can I accomplish that? Or is there a better way of stubbing out certain methods in certain scenarios that I am overlooking?

You could take inspiration from this great SO answer, and make good use of ActionDispatch::Callbacks.

ActionDispatch::Reloader.cleanup!
ActionDispatch::Reloader.prepare!
I posted the rationale behind this over here: Why does code need to be reloaded in Rails 3?
If its bad to cross post the same answer, kindly let me know how its preferred to post an answer thats relevant to two questions...I'm happy to oblige.

As of newer Rails (> 4.0), I was able to reload class definitions, in console, for instance, with reload!.

Related

rails don't calls the engine's controller

I am trying to define some helper methods to be used in the app's controller, but it seems that rails don't even call the controller. just for the test I have the following controller in my app/controllers/my_engine/application_controller.rb and as the documents say rails should find it first and an error should raise because THIS_SHOULD_PRODUCE_ERROR is unknown, but the rspec happily executing without any errors!
class ApplicationController < ActionController::Base
THIS_SHOULD_PRODUCE_ERROR
end
I even tried to mimic the devise's way but the results are the same!
The guide section on the app directory suggests that the application_controller in an engine "will provide any common functionality for the controllers of the engine".
So I wouldn't expect that any additions to that controller will be available to all controllers in an application.
That also means that your application_controller is, I suspect, not getting called when you're running your test. Which would explain why you're not seeing an error.
In terms of how devise does it I think you need to be looking at how define_helpers works. The code you've linked to in your question is the application controller in the test app for the devise gem.
I noticed that I have got things wrong, and the application_controller in the engine does not get applied to application_controller in the app! Also, I couldn't figure out how the devise did it, but I have come up with the simple workaround for this:
require_relative 'controllers/helpers'
module Acu
module Injectors
class << self
ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |**args|
eval((Acu::Configs.get :base_controller).to_s).class_eval do
include Acu::Controllers::Helpers
end
end
end
end
end
This will inject controller helpers to the user's base controller (which I get from the user, default: :ApplicationController) at the end of the class, which is perfect for me (but don't know how to add it to begging of the class if anyone needs it)

How do I filter or remove logging of ActiveJob arguments?

I'm using Rails' ActiveJob, and one of my jobs take a raw email as input. When debugging, this can result in a huge amount of noise in my application log. How can I avoid that?
[ActiveJob] Enqueued EmailParserJob (Job ID: 9678f343-c876-4f9f-9cc7-db440634e178) to DelayedJob(default) with arguments: "NOISE"
See https://github.com/rails/rails/blob/4-2-stable/activejob/lib/active_job/logging.rb#L10
ActiveJob::Base.logger = Logger.new(nil)
One thing that may be useful to note here: In any instance of a class that is subclassed from (Rails 5.1) ActiveJob::Base (or, any class instance called by a class subclassed from ActiveJob::Base) The normal Rails.logger.info('log this') commands are going to get logged to the rails console (presumably via STDOUT).
I haven't quite figured out the mechanism that causes this hijacking of Rails.logger, but you can switch to ActiveJob::Base.logger and use the knowledge of this: (https://github.com/rails/rails/blob/b205ea2dc6c70b2b8e2134640e3056ed33fdc6be/activejob/lib/active_job/logging.rb#L13) to change the behavior as you wish.
So, this allows you to log as you want:
1) Include require "active_job/logging" in your application.rb
2) In config/development.rb (or whatever environments you want) include this line:
config.active_job.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new("log/#{Rails.env}.log"))
3) Any logging inside of subclasses of ActiveJob, use this for logging:
ActiveJob::Base.logger.info('(MyJob) Inside of a job but not going to STDOUT')
If anyone can point out the code that explains why Rails.logger.info behaves differently when inside of an ActiveJob class that would be some good reading.
It seems the only way is to override ActiveJob's internal logging method:
class ActiveJob::Logging::LogSubscriber
private def args_info(job)
''
end
end
Put it somewhere into app/initializers/active_job_logger_patch.rb.
I used after_initialize to hook beforehand. It turned out to work only in perform_start method but not enqueue.
Using on_load method to hook works. I think it's the lazyload feature in Rails causing the class to be loaded after the override.
ActiveSupport.on_load :active_job do
class ActiveJob::Logging::LogSubscriber
private def args_info(job)
# override this method to filter arguments shown in app log
end
end
end
From Rails 6.1, you can turn off the logging with log_arguments like the below. This will turn off logging the arguments for all the jobs derived from ApplicationJob, You can also set it on per job basis.
class ApplicationJob < ActiveJob::Base
self.log_arguments = false
end
Reference:
https://github.com/rails/rails/pull/37660
It looks like they added the feature Add an option to disable logging for jobs with sensitive arguments in Rail 6. This would prevent any arguments from appearing in the log lines.
Edit: To use this with ActionMailer, using a custom mailer job might work, haven't tested this myself yet.
Found the following recommendation here:
ActiveJob::Base.logger = Logger.new(IO::NULL)
Seems to be better than passing a nil

How does RoR's "magic" namespace resolution work?

I've come from using Python and I'm very confused as to how the "magic" of Ruby on Rails works.
1. There are no require statements anywhere
In Python, in order to access functions from anywhere, you must import. I assume it's the same for base ruby. But when using rails, I can call hidden variables and functions, defined in other modules, without any require statements on the top of the page.
E.g. I can have a file as such:
class CartsController < ApplicationController
....
def show
begin
#cart = Cart.find(params[:id])
rescue ActiveRecord::RecordNotFound
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to store_url, notice: 'Invalid cart'
end
end
Where logger, redirect, and the such are all not defined. Does it simply inherit from ApplicationController up some convoluted tree, or does it somehow access those namespaces through other mechanisms?
2. Using methods that don't exist
This is valid rails code
current_item = line_items.find_by_product_id(product_id)
where find_by_products_id has not been defined anywhere and yet, Rails somehow dynamically "creates" the method on the fly. Any technical insight on how this is done?
Thanks for your help!
Rails' "Magic" makes extensive use of method_missing and const_missing.
When you try and call a method that is not defined, ruby fires a call to method_missing.
This is used by libraries like ActiveRecord to implement dynamic finders.
method_missing example:
SomeModel.find_by_some_field("some_value") is not defined.
This calls SomeModel.method_missing(:find_by_some_field, "some_value").
ActiveRecord then translates this call to `SomeModel.where(:some_field => "some_value")
(for performance purposes, ActiveRecord then dynamically defines this method, so next time find_by_some_field is defined)
const_missing example:
SomeModel has not yet been required.
The Ruby interpreter calls const_missing with the param "SomeModel"
Rails follows the convention "SomeModel" should be defined in a file called some_model.rb so const_missing just tries require "some_model".
Ok. You asked a lot of questions in one. It will be hard to explain all ruby and rails magic in one answer but I'll try to give you some useful resources where you can find some answers.
1) About require statements. If you are new to ruby and rails it will be hard to understand how rails application is initialized. Here is a tutorial were you can some useful information:
http://guides.rubyonrails.org/initialization.html
If you need more information about particular method you can always look into documentation. For example redirect_to method info:
http://apidock.com/rails/ActionController/Base/redirect_to
2) About "Using methods that don't exist". This is one of the most beautiful feature of ruby language called metaprogramming. This is also advanced topic. Here are some useful resources:
http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Pros/dp/1934356476
Ruby metaprogramming online tutorial
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/
http://rubylearning.com/blog/2010/11/23/dont-know-metaprogramming-in-ruby/
Since neither of the existing answers mention it: yes, things like redirect_to are inherited by way of ApplicationController. In this particular case, redirect_to is defined in the module ActionController::Redirecting, which is included in ActionController::Base (from which ApplicationController inherits).

Stubbing Warden on Controller Tests

I'm having an issue with testing my controllers and using Warden.
All examples point at stubbing request.env['warden']. This causes issues in my controllers when I call env['warden'], which then returns nil.
For a crude example, using this:
request.env['warden'] = double(Warden, :authenticate => nil,
:authenticate! => nil,
:authenticated? => false)
And a simple before filter like this:
before_filter do
redirect_to new_user_session_url unless env['warden'].authenticated?
end
I get a nil.
I just managed to get it working using controller.env['warden'] = ... and it works.
This makes sense, since it's sitting right at the controller level, so I guess my question is what wouldn't it work in the I've seen all examples.
I have this in my spec_helper:
config.include Warden::Test::Helpers
Any help would be great!
I wrote controller test helpers for Warden.
http://kentaroimai.com/articles/1-controller-test-helpers-for-warden
Despite many examples telling you to implement Warden through env['warden'] in your Rails app. It seems the correct way to access it through request.env['warden'].
It found this out by raising env in my controllers during tests, and this always came out nil.
It seems in Warden, https://github.com/hassox/warden/blob/master/lib/warden/proxy.rb#L13
There is an accessor for the rack environment, which won't exist in test mode due to the absence of Rack in controller tests. Please someone check this.
So when stubbing request.env in RSpec, your implementation needs to point at request.env.
It seems a necessary evil in my mind. But if there is anyone with a good explanation or work around, I'd love to continue this discussion.

Ruby on Rails: alias_method_chain, what exactly does it do?

I've tried reading through various blog posts that attempt to explain alias_method_chain and the reasons to use it and not use it. In particular, I took heed to:
http://weblog.rubyonrails.org/2006/4/26/new-in-rails-module-alias_method_chain
and
http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/
I still do not see any practical use for alias_method_chain. Would anyone be able to explain a few things.
1 - is it still used at all?
2 - when would you use alias_method_chain and why?
1 - is it still used at all?
Apparently yes, alias_method_chain() is still used in Rails (as of version 3.0.0).
2 - when would you use
alias_method_chain and why?
(Note: the following is largely based on the discussion of alias_method_chain() in Metaprogramming Ruby by Paolo Perrotta, which is an excellent book that you should get your hands on.)
Let's start with a basic example:
class Klass
def salute
puts "Aloha!"
end
end
Klass.new.salute # => Aloha!
Now suppose that we want to surround Klass#salute() with logging behavior. We can do that what Perrotta calls an around alias:
class Klass
def salute_with_log
puts "Calling method..."
salute_without_log
puts "...Method called"
end
alias_method :salute_without_log, :salute
alias_method :salute, :salute_with_log
end
Klass.new.salute
# Prints the following:
# Calling method...
# Aloha!
# ...Method called
We defined a new method called salute_with_log() and aliased it to salute(). The code that used to call salute() still works, but it gets the new logging behavior as well. We also defined an alias to the original salute(), so we can still salute without logging:
Klass.new.salute_without_log # => Aloha!
So, salute() is now called salute_without_log(). If we want logging, we can call either salute_with_log() or salute(), which are aliases of the same method. Confused? Good!
According to Perrotta, this kind of around alias is very common in Rails:
Look at another example of Rails
solving a problem its own way. A few
versions ago, the Rails code contained
many instances of the same idiom: an
Around Alias (155) was used to add a
feature to a method, and the old
version of the method was renamed to
something like
method_without_feature(). Apart from
the method names, which changed every
time, the code that did this was
always the same, duplicated all over
the place. In most languages, you
cannot avoid that kind of duplication.
In Ruby, you can sprinkle some
metaprogramming magic over your
pattern and extract it into its own
method... and thus was born
alias_method_chain().
In other words, you provide the original method, foo(), and the enhanced method, foo_with_feature(), and you end up with three methods: foo(), foo_with_feature(), and foo_without_feature(). The first two include the feature, while the third doesn't. Instead of duplicating these aliases all around, alias_method_chain() provided by ActiveSupport does all the aliasing for you.
alias_method_chain has been deprecated in Rails 5 in favour of Module#prepend.
Pull request: https://github.com/rails/rails/pull/19434
Changelog: https://github.com/rails/rails/blob/b292b76c2dd0f04fb090d49b90716a0e6037b41a/guides/source/5_0_release_notes.md#deprecations-4
I'm not sure if it's gone out of style with Rails 3 or not, but it is still actively used in versions before that.
You use it to inject some functionality before (or after) a method is called, without modifying any place that calls that method. See this example:
module SwitchableSmtp
module InstanceMethods
def deliver_with_switchable_smtp!(mail = #mail)
unless logger.nil?
logger.info "Switching SMTP server to: #{custom_smtp.inspect}"
end
ActionMailer::Base.smtp_settings = custom_smtp unless custom_smtp.nil?
deliver_without_switchable_smtp!(mail = #mail)
end
end
def self.included(receiver)
receiver.send :include, InstanceMethods
receiver.class_eval do
alias_method_chain :deliver!, :switchable_smtp
end
end
end
That's an addition to ActionMailer to allow swapping out of the SMTP settings on each call to deliver!. By calling alias_method_chain you are able to define a method deliver_with_switchable_smtp! in which you do your custom stuff, and call deliver_without_switchable_smtp! from there when you're done.
alias_method_chain aliases the old deliver! to your new custom method, so the rest of your app doesn't even know deliver! now does your custom stuff too.
is it used at all?
Seems so. It's a common practice among Rails developers
when would you use alias_method_chain and why?
Despite the warnings, alias_method_chain is still the main strategy used when injecting functionality to an existing method, at least was in Rails 2.x and is followed by many people extending it. Yehuda ought to remove alias_method_chain from rails 3.0 to say from his posts and comments in Rails tickets. It is still used by many extensions that add custom behavior at certain points of the execution, such as loggers, error reporters, benchmarking, data injection, etc.
IMO, the best alternative is to include a module, thus you have decoration over delegation. (For example, follow example 4 in this post). That way you can alter the objects even individually if you'd like, without polluting the class' methods. The downside to this is that the method lookup chain increases for each module you inject, but this is what modules are for anyway.
Very interesting question, will keep a look on what other people think about it.

Resources