ActiveSupport::Notifications access payload from inside block - ruby

I recently found out about ActiveSupport::Notifications. I think it's a great tool to monitor your applications.
I am currently using it outside of Rails to monitor my Sinatra app and it's working great.
However I use it a lot, to instrument custom queries and would like to somehow notify the result of a query:
my_input = "some_query"
ActiveSupport::Notifications.instrument("myapp.create", :input => my_input) do
#stuff
result = search_for my_input
#stuff
result
end
Now I can subscribe to this and can also get the query that was executed (which is available in the payload hash). But I would also like to see the result in my subscriber.
So is there a way to add any custom value to the payload while I am executing the block?

Just stumbled upon your question when I was looking for the same thing.
You can simply pass in a block variable, it will represent your payload to you, and you can fill it up while you're in the block
my_input = "some_query"
ActiveSupport::Notifications.instrument("myapp.create", :input => my_input) do |instrument|
#stuff
result = search_for my_input
instrument[:my_result] = result
#stuff
result
end
In your subscriber create a new event from the passed argument:
ActiveSupport::Notifications.subscribe("myapp.create") do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
Rails.logger.info event.payload
end

Related

Sinatra Multiple Parallel Requests Variable Behaviour

I am fairly new to ruby and would like to understand how class instance variables behave in case of multiple parallel requests.
I have a method inside my controller class which is called everytime for each request for a specific operation (create in this case)
class DeployProvision
def self.create(data)
raise "Input JSON not received." unless data
# $logger.info input_data.inspect
failure = false
response_result = ""
response_status = "200"
#validator = SchemaValidate.new
validation = #validator.validate_create_workflow(data.to_json)
end
end
This method is called as (DeployProvision.create(data))
I am a little confused on how #validator class instance variable behaves when multiple requests come. Is it shared among multiple requests. Is it a good idea to declare this as class instance variable instead of a local variable ?
I am working on an existing code base and would like to understand the intent of creating #validator as a class instance variable instead of local variable.
You can write ultra-simple script like this:
require 'sinatra'
class Foo
def self.bar
#test = Time.now
puts #test
end
end
get '/' do
Foo.bar
end
and you'll see it does nothing, because with every call, you're creating new instance of Time(SchemaValidate in your code).
If you used memoization and had something like #validator ||= SchemaValidate.new you would have one instance of SchemaValidate stored between requests.
I don't think that'd change anything in terms of performance and I don't have idea why would anyone do something like that.
You can have some fun with ultra-simple scripts with sinatra to test how it behaves.
Good luck with this code!

I need a class to call a class containing it as a property for a number of scenarios - how should this be implemented?

I'm fairly new to Ruby, coming from Objective-C and Swift.
I've hit upon a problem where I have an object containing another object as a property. The second object has a beginStream function, by which it streams data from a server, and when it gets new data then it yields, thus the first object can respond. It looks like this:
class StreamManager
def initialize
#streams = Array.new
end
def setup_user_stream(user_id)
stream = Stream.new(user_id)
#streams << stream
stream.begin_stream do |message|
puts "A message was received: #{message}"
end
end
end
class Stream
def initialize(user_id)
#user_id = user_id
end
def begin_stream
Thread.new do
# Begins stream
#client = Stream::Client.new(user_id)
#client.on_error do
# need to let StreamManager know about this
end
#client.on_message do |message|
yield message if block_given?
end
end
end
end
Now I have this on_error call that I'm getting from my stream client, and I need to let my StreamManager know about it. How would I go about doing this?
In Objective-C/Swift, I'd have a protocol called StreamDelegate, which the stream would have as a weak property, and then the StreamManager would set the Stream's delegate to be itself, and respond to the functions provided in the protocol. So then the Stream would call the delegate function #delegate?.streamDidReceiveError, and the stream manager would be set as the delegate, and have that function implemented, and it'd be called.
I've simplified this example - the Stream is an abstraction over the Stream::Client, which is from another library and also gives out a bunch of other messages. But now I'm writing this, I'm thinking perhaps their way of having those different blocks I'm yielding is the way to go. In which case, I'd need to understand how to implement that myself? Or perhaps that would be a poor way to design my class - I don't know?
There's a few different approaches here:
1) You could still use the delegate pattern you're used to in Cocoa. The only difference is you wouldn't have a formal protocol/interface for it. Your Stream would take a delegate/callback handler object which is any object that implements certain methods. You can make those methods optional by checking the object responds to them before calling them. Your StreamManager could implement this interface and pass itself in to the stream as a dependency.
2) You could define callbacks on your Stream class for errors and messages rather than passing a single block to the begin_stream method.
3) Keep your existing API by instead of yielding the message, encapsulate the message or error in a Result object and yield that instead. I think this might be my preferred option.
Sorry for the lack of code examples but I'm writing this on my iPhone.
The parent StreamManager can pass itself as a variable to the child Stream.
def initialize(stream_manager, user_id)
#stream_manager = stream_manager
#user_id = user_id
end
And the initializing it, in setup_user_stream:
stream = Stream.new(self, user_id)
If you want slightly more verbose code, you can use named keywords:
def initialize(:stream_manager, :user_id)
then:
Stream.new(stream_manager: self, user_id: user_id)

Sinatra pass anonymous block

I am new to Sinatra and am trying to implement the following:
REST service with get method whose action block can be provided. Something like:
class C1
get '/something' do
<some action to be provided later>
end
post '/something' do
<some action to be provided later>
end
end
C1.new
C1.get_block = { "Hello from get" }
C1.post_block = { "Hello from post" }
Is it possible to do something like above? I am designing an intercepting service which can be used to perform different actions depending on conditions
The following should do the trick. I've added arrays so you can register multiple blocks that should be executed upon a GET/POST requests.
class C1
##get_blocks = []
##post_blocks = []
def register_get(block)
##get_blocks << block
end
def register_post(block)
##post_blocks << block
end
get '/something' do
##get_blocks.each do |block|
block.call(params)
end
end
post '/something' do
##post_blocks.each do |block|
block.call(params)
end
end
end
C1.new
C1.register_get { |params| "Hello from get" }
C1.register_post { |params| "Hello from post" }
I think #MartinKonecny answered the question using a very nice dynamic and effective approach...
...But please allow me to suggest a different approach, assuming that the code itself is static and that it is activated according to a set of conditions.
I know that using the Plezi framework you could alternate controllers for the same route, so that if one controller doesn't answer the request, the next one is tested.
I believe that Sinatra could be used in the same way, so that when the first route fails, the second route is attempted.
A short demonstration of the concept (not an actual app code), using the Plezi framework would look something like this:
require 'plezi'
listen
class C1
def index
"Controller 1 answers"
end
def hello
false
end
end
class C2
def show
"Controller 2 shows your request for: #{params[:id]}"
end
def hello
'Hello World!'
end
end
route '/(:id)', C1
route '/(:id)', C2
exit # exit the terminal to test this code.
This way, for the path localhost:3000/, the C1 class answers. But, the route for C1 fails the restful request for the path localhost:3000/1, so the route for C2 answers that request, attempting to show object with id==1.
This is easy to see when accessing the localhost:3000/hello route - the first route fails, and the second one is then attempted.
If you don't need to define the blocks dynamically, perhaps this approach - which I assume to be available also in Sinatra - will be more easy to code and maintain.
If this isn't available using Sinatra, you could probably imitate this approach using a "routing" class as a controller on your Sinatra app.
Another approach would be to specify the conditions in a case statement, calling different methods according to each situation.
class C1
get '/something' do
case
when true # condition
# do something
when true # condition
# do something
when true # condition
# do something
when true # condition
# do something
else
# do something
end
end
end

AFMotion HTTP GET request syntax for setting variable

My goal is to set an instance variable using AFMotion's AFMotion::HTTP.get method.
I've set up a Post model. I would like to have something like:
class Post
...
def self.all
response = AFMotion::HTTP.get("localhost/posts.json")
objects = JSON.parse(response)
results = objects.map{|x| Post.new(x)}
end
end
But according to the docs, AFMotion requires some sort of block syntax that looks and seems to behave like an async javascript callback. I am unsure how to use that.
I would like to be able to call
#posts = Post.all in the ViewController. Is this just a Rails dream? Thanks!
yeah, the base syntax is async, so you don't have to block the UI while you're waiting for the network to respond. The syntax is simple, place all the code you want to load in your block.
class Post
...
def self.all
AFMotion::HTTP.get("localhost/posts.json") do |response|
if result.success?
p "You got JSON data"
# feel free to parse this data into an instance var
objects = JSON.parse(response)
#results = objects.map{|x| Post.new(x)}
elsif result.failure?
p result.error.localizedDescription
end
end
end
end
Since you mentioned Rails, yeah, this is a lil different logic. You'll need to place the code you want to run (on completion) inside the async block. If it's going to change often, or has nothing to do with your Model, then pass in a &block to yoru method and use that to call back when it's done.
I hope that helps!

Rails 3, confused about 'before_create :some_method' ... WHEN does some_method do its thing?

we have model helper (used by several different models) called set_guids that sets self.theguid to a random string. Been using it for a long time, we know it works.
in a new model 'Dish' we created, we have
before_create :set_guids (NOTE: no other before/after/validation, just this)
def do_meat_dish
( this is invoked by #somemeat.do_meat_dish in the Dish contoller )
( it manipulated the #somemeat object using self.this and self.that, works fine)
( THEN sometimes it creates a new object of SAME MODEL type )
( which is handled differently)
#veggie = Dish.new
#veggie.do_veggie_dish
end
def do_veggie_dish
recipe_str = "add the XXXX to water"
recipe_str.gsub!("XXXX", self.theguid) *** the PROBLEM: self.theguid is nil
end
as soon as we execute veggie = Dish.new shouldn't veggie.theguid be initialized?
Note we have not saved the new object yet... but the before_create should still have done its thing, right?
it is something to do with create a new instance of a model inside a method for the same model?
is it something with using # for the variables?
Additional note: if we comment out the line trying to access self.theguid everything else works fine ... it's ONLY the value (supposedly) set by the before_create set_guids that is nil instead of being a guid.
before_create is called only before the object is saved to the database the first time. That's why you get nil.
I suggest that you use after_initialize callback instead. Be careful though, since after_initialize will be called whenever the document is new or loaded from the db, that way you will have new guids every time you get the document, which is not what you want. So I suggest you do something like:
def set_guids
return unless theguid.nil?
.....
end
As another solution, if you don't want to change the after_create callback above, you can do something like:
def theguid
super || set_guids
end
That should let you go also.

Resources