'on raise' hook in ruby - ruby

Is there a way to hook in some code after any raise is done in ruby? The idea is to provide some extra information about the error in a centralized location, such as follows:
def after_raise
logger.error "some information here"
end
def logger
#logger ||= Logger.new(my_log_output)
end
Edit: Maybe it will help if I'm more specific about what this is for.
I'm working a large project with multiple domains. There are times that raised errors are just not descriptive enough - today I was talking with one of my team-members and we discussed ways to work around this. It's not that the original raise messages are awful/completely nondescript (although, I'm sure that's the case in one or two places), just that as the project has evolved, new information has become useful for troubleshooting. We'd like to create something that can extend existing code.
Each domain is able to be tested alone, but plugs into an overall infrastructure. If we added this to the 'main' domain, when the application as a whole is running (for example, during end-to-end tests) this would be applied to all instances where raise is called. If we were testing a single domain, it would not apply.

You can redefine the Kernel#raise method, keeping the original method through an alias:
module Kernel
alias_method :_raise, :raise # original method is now _raise
def raise(*args)
# do hook stuff
_raise(*args)
end
end

Related

Nesting modules in classes and directly calling module functions

For testing and administration purposes I am looking to build a class to communicate with an API. I've got the connection and authentication down but am struggling with the base structure and size of the class.
My main goal is to keep each application domain split, but still easy to access by one class/connection.
I've made an simpler example of what I'm looking for. In reality each domain has its own set of business rules to follow, which is why I want to keep them separate, whilst the API connection stays the same.
For instance, on CLI level I want to invoke:
$ client_one = Api.new("one")
$ client_two = Api.new("two")
$ client_one.Bikes.delete(1)
> deleted bike 1 from one
$ client_two.Phones.new(phone)
> posted phone iPhone to two
My thought proces was to nest modules inside an Api class but I can't get it to work or find the right syntax.
class Api
def initialize(client)
#client = client
#connection = Authentication.get_connection(#client)
end
#preferable put each submodule in a separate file
module Authentication
def get_connection(client)
#code to get Faraday connection
end
end
module Bikes
def new(object)
#code to post new bike
#connection.post(object)
puts "posted bike #{object.name} to #{#client}"
end
def delete(id)
#code to delete old bike
#connection.delete(id)
puts "deleted bike #{id} from #{#client}"
end
end
module Phones
def new(object)
#code to post new phone
#connection.post(object)
puts "posted phone #{object.name} to #{#client}"
end
end
end
This results in errors like:
NoMethodError: undefined method `Bikes' for #<Api:0x0000000003a543a0>
Is it possible to achieve my goal or are there better 'Ruby' ways to accomplish it?
Furthermore, is it possible to split the submodules to different files? eg:
api.rb
modules
+ -- authentication.rb
+ -- bikes.rb
+ -- phones.rb
There are some fundamental misconceptions of how Ruby OOP works in your example, and without a full code sample and the opportunity to interrogate you about what you're trying to accomplish it's hard to guide you to what might be the most appropriate answer. Any answer I give will be based partly on experience and partly on opinion, so you may see other answers as well.
At a high level, you should have classes in modules and not modules in classes. Although you can put modules in classes you better have a good understanding of why you're doing that before doing it.
Next, the modules and methods you've defined in them do not automatically become accessible to instances of the parent class, so client.Bikes will never work because Ruby expects to find an instance method named Bikes inside the Api class; it won't look for a module with that name.
The only way to access the modules and module methods that you have defined is to use them at the class/module level. So if you have this:
class Foo
module Bar
def baz
puts 'foobarbaz'
end
end
end
You can do this at the class/module level:
Foo::Bar.baz
foobarbaz
=> nil
But you can't do anything at the instance level:
Foo.new::Bar.baz
TypeError: #<Foo:0x00007fa037d39260> is not a class/module
Foo.new.Bar.baz
NoMethodError: undefined method `Bar' for #<Foo:0x00007fa037162e28>
So if you understand so far why the structure of your example doesn't work, then you can work on building something a little more sensible. Let's start with naming and the class/module structure.
First, Api is a poor name here because you'll typically use Api for something that provides an API, not connects to one, so I would recommend making the name a bit more descriptive and using a module to indicate that you are encapsulating one or more related classes:
module MonthyApiClient
end
Next, I'd recommend adding a Client class to encapsulate everything related to instantiating a client used to connect to the API:
module MonthyApiClient
class Client
def initialize
#client = nil # insert your logic here
#connection = nil # insert your logic here
end
end
end
The relationship between client and connection in your code example isn't clear, so for simplicity I am going to pretend that they can be combined into a single class (Client) and that we are dropping the module Authentication entirely.
Next, we need a reasonable way to integrate module Bikes and module Phones into this code. It doesn't make sense to convert these to classes because there's no need to instantiate them. These are purely helper functions that do something for an instance of Client, so they should be instance methods within that class:
module MonthyApiClient
class Client
def initialize
# insert your logic here
#client = nil
#connection = nil
end
def create_bike
# insert your logic here
# e.g., #connection.post(something)
end
def delete_bike
# insert your logic here
# e.g., #connection.delete(something)
end
def create_phone
# insert your logic here
# e.g., #connection.post(something)
end
end
end
Note that we've swapped new for create; you don't want to name a method new in Ruby, and in the context we're using this new would mean instantiate but do not save a new object whereas create would mean instantiate and save a new object.
And now that we're here, and now that we've eliminated all the nested modules by moving their logic elsewhere, we can see that the parent module we set up originally is unnecessarily redundant, and can eliminate it:
class MonthyApiClient
def initialize
# insert your logic here
#client = nil
#connection = nil
end
def create_bike
# insert your logic here
# e.g., #connection.post(something)
end
def delete_bike
# insert your logic here
# e.g., #connection.delete(something)
end
def create_phone
# insert your logic here
# e.g., #connection.post(something)
end
end
Then you can accomplish your original goal:
client_one = MonthyApiClient.new
client_one.create_bike
client_two = MonthyApiClient.new
client_two.create_phone
Having worked through this explanation, I think your original code is an example of spending a lot of time trying to over-optimize prematurely. It's better to plan out your business logic and make it as simple as possible first. There's some good information at https://softwareengineering.stackexchange.com/a/80094 that may help explain this concept.
I've even skipped trying to optimize the code I've shown here because I don't know exactly how much commonality there is between creating and deleting bikes and phones. With this functional class, and with a better understanding of other code within this app, I might try to DRY it up (and that might mean going back to having a module with a Client class and either module methods or other classes to encapsulate the DRY logic), but it would be premature to try.
Your last question was about how to structure files and directories for modules and classes, and I would refer you to Ideal ruby project structure (among many other questions on this site) for more information.

RSpec stubs apparently not cleaned up after test

I've been working to diagnose a test failure that only occurs on my master branch. Following is the relevant code, in simplified form.
There's a service:
class Service
attr_reader :traces
def initialize
#traces = []
end
def do_work
#traces << Thread.current.backtrace
# ... actual work ...
end
end
And a class that makes use of the service:
class Widget
def get_cached_service
puts("Getting a cached service!")
puts("Do I already have one? #{!!#service}")
#service ||= make_service
end
def make_service
puts("Making a service!")
Service.new
end
end
I have a test (that lives in a file widget_spec.rb) that fails intermittently. This test creates an instance of Widget and calls get_cached_service. I see the Getting a cached service! message on the console, followed by Do I already have one? false, but I don't see the Making a service! message.
Furthermore, when I examine the traces attribute of the returned Service object, I find stack traces originating from other tests in my project (eg. foo_spec.rb, bar_spec.rb, etc).
In a few different places I find code like:
allow_any_instance_of(Widget)
.to receive(:make_service).and_return(whatever)
The other tests whose stack traces I find are likely stubbing make_service like this. But it appears that the stubbing is not being undone after those tests, as should always happen according to my understanding.
Is there any reason, other than a bug in rspec, that could cause a stub not to be reset at the end of a test?
The stub is almost certainly being cleared, but you’ve cached the fake instance in get_cached_service. Nothing clears the cached value in #service, and RSpec (rightfully) doesn’t know about it. As such, stubbing make_service is not enough if tests call get_cached_service. You have a few options:
Always stub get_cached_service instead of, or in addition to, make_service
Provide a way to clear the cached value which is called after each test.
Make the caching configurable in some way, or a wrapper around the actual implementation, such that the caching does not occur in test code.
I realise this is quite late to answer, but for posterity for anyone who reads this:
Use rspec bisect to figure out if there is a consistent test ordering that causes failure, then start ripping code out until you're left with only the bit that breaks.
I can't remember a case where RSpec is at fault - almost invariably, somewhere there is a class variable that isn't getting cleared, or someone is manually playing with a class with something like define_method. Occasionally it might be happening in a gem.
Make sure everything is cleared after every test in your spec_helper - clear the Rails cache, clear ActionMailer deliveries, return from Timecop freezes, etc.
Anything directly RSpec-related should clear itself in theory, because it's designed to integrate into RSpec, and is probably the least likely explanation in general.

A better way to call methods on an instance

My question has a couple layers to it so please bear with me? I built a module that adds workflows from the Workflow gem to an instance, when you call a method on that instance. It has to be able to receive the description as a Hash or some basic data structure and then turn that into something that puts the described workflow onto the class, at run-time. So everything has to happen at run-time. It's a bit complex to explain what all the crazy requirements are for but it's still a good question, I hope. Anyways, The best I can do to be brief for a context, here, is this:
Build a class and include this module I built.
Create an instance of Your class.
Call the inject_workflow(some_workflow_description) method on the instance. It all must be dynamic.
The tricky part for me is that when I use public_send() or eval() or exec(), I still have to send some nested method calls and it seems like they use 2 different scopes, the class' and Workflow's (the gem). When someone uses the Workflow gem, they hand write these method calls in their class so it scopes everything correctly. The gem gets to have access to the class it creates methods on. The way I'm trying to do it, the user doesn't hand write the methods on the class, they get added to the class via the method shown here. So I wasn't able to get it to work using blocks because I have to do nested block calls e.g.
workflow() do # first method call
# first nested method call. can't access my scope from here
state(:state_name) do
# second nested method call. can't access my scope
event(:event_name, transitions_to: :transition_to_state)
end
end
One of the things I'm trying to do is call the Workflow#state() method n number of times, while nesting the Workflow#event(with, custom_params) 0..n times. The problem for me seems to be that I can't get the right scope when I nest the methods like that.
It works just like I'd like it to (I think...) but I'm not too sure I hit the best implementation. In fact, I think I'll probably get some strong words for what I've done. I tried using public_send() and every other thing I could find to avoid using class_eval() to no avail.
Whenever I attempted to use one of the "better" methods, I couldn't quite get the scope right and sometimes, I was invoking methods on the wrong object, altogether. So I think this is where I need the help, yeah?
This is what a few of the attempts were going for but this is more pseudo-code because I could never get this version or any like it to fly.
# Call this as soon as you can, after .new()
def inject_workflow(description)
public_send :workflow do
description[:workflow][:states].each do |state|
state.map do |name, event|
public_send name.to_sym do # nested call occurs in Workflow gem
# nested call occurs in Workflow gem
public_send :event, event[:name], transitions_to: event[:transitions_to]
end
end
end
end
end
From what I was trying, all these kinds of attempts ended up in the same result, which was my scope isn't what I need because I'm evaluating code in the Workflow gem, not in the module or user's class.
Anyways, here's my implementation. I would really appreciate it if someone could point me in the right direction!
module WorkflowFactory
# ...
def inject_workflow(description)
# Build up an array of strings that will be used to create exactly what
# you would hand-write in your class, if you wanted to use the gem.
description_string_builder = ['include Workflow', 'workflow do']
description[:workflow][:states].each do |state|
state.map do |name, state_description|
if state_description.nil? # if this is a final state...
description_string_builder << "state :#{name}"
else # because it is not a final state, add event information too.
description_string_builder.concat([
"state :#{name} do",
"event :#{state_description[:event]}, transitions_to: :#{state_description[:transitions_to]}",
"end"
])
end
end
end
description_string_builder << "end\n"
begin
# Use class_eval to run that workflow specification by
# passing it off to the workflow gem, just like you would when you use
# the gem normally. I'm pretty sure this is where everyone's head pops...
self.class.class_eval(description_string_builder.join("\n"))
define_singleton_method(:has_workflow?) { true }
rescue Exception => e
define_singleton_method(:has_workflow?) { !!(puts e.backtrace) }
end
end
end
end
# This is the class in question.
class Job
include WorkflowFactory
# ... some interesting code for your class goes here
def next!
current_state.events.#somehow choose the correct event
end
end
# and in some other place where you want your "job" to be able to use a workflow, you have something like this...
job = Job.new
job.done?
# => false
until job.done? do job.next! end
# progresses through the workflow and manages its own state awareness
I started this question off under 300000 lines of text, I swear. Thanks for hanging in there! Here's even more documentation, if you're not asleep yet.
module in my gem

How to separate core logic and logging code in a Ruby application?

I am looking for a general way to maintain a separation of core logic and logging/debugging/terminal output code in a Ruby application's codebase. Ideally, I would like to have a separate "tracer" codebase parallel to that of my application core (the 'lib' directory in a typical Ruby project). The tracer code would live in a special directory (perhaps called "trace") in the same way that unit tests often live in a parallel structure in the test/spec directory. Tracer files would extend target classes with wrappers for various methods. These wrappers would do things like writing to a log, setting a breakpoint (e.g. with pry's' binding.pry) or incrementing a progress bar whenever the method returned. The loading of the tracer code could then be controlled by a single switch.
I have done some research and come up mostly blank. I've found some pieces of the functionality I'm after; for example, the standard library's Tracer class and the method_decorators gem. But I am wondering there is a more complete solution out there, something analogous to rspec for testing. Does such a thing exist? Or are there perhaps other ways of dealing with this problem?
If you, for instance, are looking at enabling this code only in development, then you can add an initializer in rails: /config/initializers
if( Rails.env.development?)
require "logging_wrappers"
end
In your lib/ folder, you can add your extensions into lib/logging_wrappers.rb
To override the find_by_id function on your User model, simply add the following:
class User
alias_method :unlogged__find_by_id, :find_by_id
def find_by_id(id)
Rails.logger.info "About to find by id"
x = unlogged__find_by_id(id)
if (x.blank?)
Rails.logger.info "We didn't find any users"
end
end
For more information on this technique, several good examples can be found at: http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method/

Detecting Ruby mixin being included

I have a question similar to this one. I'm writing an app that has an addon system. There's an Addon mixin module, which detects when it's included and registers the new addon automatically:
module Addon
def self.included(receiver)
addon = receiver.new # Create an instance of the addon
(snip) # Other stuff to register the addon
addon.on_register # Tell the instance it was registered
end
end
Here is an example of how the mixin would be used:
class MyAddon
def on_register
puts "My addon was registered"
end
include Addon # ** note that this is at the end of the class **
end
As noted above, this implementation requires that the include be at the bottom of the class. Otherwise on_register isn't defined at the time that self.included gets called.
My concern is that an addon developer might accidentally put the include at the top, causing the addon not to work. Or that there might be a derived class or something that would extend the MyAddon class after it was already included.
Is there a better way to go about this?
After distilling the other answers and some other info I found, I wanted to document the answer that ended up working for me.
As this question explains, you can't detect at include() time when a class is "done" being defined. So, relying on the 'included' callback to create the objects was not a very robust solution.
The solution instead was to discover all of the addons and instantiate them after everything had been loaded. The only constraint placed on addon developers was that their code had to share a common top-level module namespace.
I still don't know if this is the best way to do it, but it definitely works better than what I started with.
Here is the code that searches the module for addons. It starts at a passed-in namespace and searches recursively for classes that include the addon class:
def find_and_instantiate(mod, addons)
consts = mod.constants
consts.each do |c|
sym = mod.const_get(c)
if (sym.class == Module)
find_and_instantiate(sym, addons)
else
if (sym.class == Class && sym.include?(Addon))
addons << sym.new(#container)
end
end
end
end
The best I can come up with is to notify the user of your module that it needs to be included after the #on_register method has been declared:
module Addon
def self.included(receiver)
raise "include #{self.name} after the #on_register method is defined" unless receiver.method_defined? :on_register
receiver.new.send(:on_register)
end
end
This is less than ideal but it will prevent aggravating mistakes until you discover a better way of doing this.
Sorry, but I have to hand this to you. So you want to hear your code is clever, pal. Yes, it is, it is too clever. But if you want to be concerned about other developers, you will have to stop being clever and start being normal.
Ruby is an OO language. The basis of OOP is that I define my object structure and their methods first, and then, when everything is defined, I instantiate my world ideally with a single call MyApp.new. That way, it does not matter what I have defined first. 'include' calls serve to establish the module inheritance hierarchy, nothing else.
But no, you do not want to do OOP. You want to abuse the included hook and instantiate receiver (!) in it, and call its instance methods. That's what they call obfuscated code. In other words, you go from OOP back to the old good goto style directive programming. Ruby allows you to obfuscate, not to limit your abilities, but with great power great responsibility, you yourself must exercise moderation.
You will yoursef have to refactor your code to make it normal. We cannot do that for you, because we don't know what do you want to achieve. Who knows, maybe you idea is worthy of a genius. Too bad we don't know it from your question. Some addon system you have. I'm asking you: Do you need it? Isn't good old mixin system good enough for you? Are you sure you are not programming Java in Ruby? And, then, is your app going to be greater than Rails? If yes, go ahead with the addon system. If not – stop playing around and invest your time in writing the algos that actually do what your app is suppose to do rather than creating inextricable addon systems in advance. If you are working in a company - which I doubt - make sure you let your boss read this.

Resources