Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish lifecycle events to routemaster #19

Merged
merged 35 commits into from
Aug 8, 2017

Conversation

gregbeech
Copy link
Contributor

@gregbeech gregbeech commented Apr 18, 2017

Here's a proposal for how publishing events could look from model classes. It's based on the code in orderweb but I've cleaned up the naming/implementation and made it more amenable to publishing multiple events from a single model by making multiple publishers the default.

Has an ActiveRecord module, a basic publisher implementation, and a
registry for publishers. Still needs a bunch more work, but this should
be a slightly cleaner version of what’s in orderweb geared towards more
complex publishing and multi-publishing.
module LifecycleEvents
extend ActiveSupport::Concern

ROUTEMASTER_EVENT_TYPE_MAP = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add a little comment — this maps ActiveRecord events to Routemaster events?

@@ -0,0 +1,49 @@
require 'active_support/concern'
require 'new_relic/agent'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a require for roo_on_rails/routemaster/publishers which is referenced.

def publish!
return unless publish?

event_data = data # cache in case it isn't memoised
Copy link
Contributor

@mezis mezis Apr 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erm, "cache" ? "memoise"? data is a stub/virtual method so this doesn't actually do anything. I think you could replace lines 18-21 with just:

routemaster.send(event, topic, url, data: recursive_stringify(event_data))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(with a caveat for the nil case of course)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be fair this pattern is copied from your code ;-)

But yes I did mean memoise. It could be cleaned by handling nil in the stringify method though.

class Publisher
attr_reader :model, :event

def initialize(model, event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest dependency-injecting a client: ::Routemaster::Client here — otherwise people are going to stub like hell when testing publisherS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair

@@ -0,0 +1,49 @@
module RooOnRails
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a require for routemaster/client.

Copy link
Contributor

@mezis mezis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A series of nitpicks but LGTM. Can't "approve" just yet, of course :P

@danielcooper
Copy link
Contributor

Seems like a good plan.

Addresses the review comments and makes things a bit tidier.
@gregbeech
Copy link
Contributor Author

@mezis @danielcooper this is going to be "interesting" to test properly as RooOnRails itself isn't a Rails app so doesn't have any database etc. to actually test an ActiveRecord model against.

The only thing I really need that for is the after_commit hook, so any objections to me faking up an after_commit hook with the same signature on the model and checking that it calls the right thing rather than going through the whole mess of actually have a real ActiveRecord model to test against?

@danielcooper
Copy link
Contributor

Can you provide a nice api for doing it, and perhaps a test helper - rather than mocking it out?

@danielcooper
Copy link
Contributor

(not that you can test after commit when you've transactional tests anyway)

@deliveroo deliveroo deleted a comment from codecov bot Jul 17, 2017
@deliveroo deliveroo deleted a comment from codecov bot Jul 17, 2017
@codecov
Copy link

codecov bot commented Jul 17, 2017

Codecov Report

Merging #19 into master will increase coverage by 0.66%.
The diff coverage is 99.52%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #19      +/-   ##
==========================================
+ Coverage   91.26%   91.92%   +0.66%     
==========================================
  Files          75       82       +7     
  Lines        1958     2131     +173     
==========================================
+ Hits         1787     1959     +172     
- Misses        171      172       +1
Impacted Files Coverage Δ
spec/roo_on_rails/routemaster/publishers_spec.rb 100% <100%> (ø)
lib/roo_on_rails/config.rb 91.66% <100%> (+1.66%) ⬆️
spec/integration/routemaster_spec.rb 100% <100%> (ø)
lib/roo_on_rails/routemaster/publisher.rb 100% <100%> (ø)
spec/roo_on_rails/routemaster/publisher_spec.rb 100% <100%> (ø)
spec/roo_on_rails/config_spec.rb 100% <100%> (ø) ⬆️
lib/roo_on_rails/routemaster/publishers.rb 100% <100%> (ø)
.../roo_on_rails/routemaster/lifecycle_events_spec.rb 100% <100%> (ø)
lib/roo_on_rails/routemaster/lifecycle_events.rb 96.29% <96.29%> (ø)
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 197ac44...fddf7bf. Read the comment docs.

Copy link
Contributor

@danielcooper danielcooper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. This will need some docs both in the README and in code docs I think.

You'll probably want some more eyes too


module ClassMethods
def publish_lifecycle_events(*events)
events ||= %i(create update destroy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be something along the lines of

    events = events.any? ? events : %i(create update destroy)

*events will produce [] with no arguments

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, described_class._commit_callbacks will give you a list of registed callbacks. You should use this in your test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, that's super helpful. I'll try that, thank you 🙂


it 'inserts Routemaster Client into the middleware stack' do
app.wait_start
expect(output).to include 'Routemaster::Client'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the client have to be in the middleware stack?

Copy link

@rfonglip rfonglip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 1 small question.

There are some models (e.g. restaurant) where we don't want to actually delete/destroy the object, so we mark it as deleted in the db.

Would roo_on_rails handle this scenario?

Else nice work!

@client = client
end

def publish?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this method always return true?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention is that it's ready to be overloaded in the clients own publishers

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that it's true by default and you can then overwrite this in your implementation. You can see this in the example I provide.

@danielcooper
Copy link
Contributor

@rfonglip

You can either overload the publisher's publish! method to switch the event type if the object has it's flag set or try and tie in with paranoia's callbacks - calling publish_lifecycle_event_on_delete (although I assume you'll need to find a way for it not to trigger an update callback).

I suppose it depends on us having a standard gem we use for soft deletes?

Copy link
Member

@scott-ad-riley scott-ad-riley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, one minor typo

Thanks for thorough the readme on this 👍

README.md Outdated
* `ROOBUS_URL` – the full URL of your Routemaster application (mandatory)
* `ROOBUS_UUID` – the UUID of your application, e.g. `logistics-dashboard` (mandatory)

If you then want to enable the publishing of events onto the event but, you need to set `ROUTEMASTER_PUBLISHING_ENABLED` to `true` and implement publishers as needed. An example of how to do this is detailed in [`README.routemaster_client.md`](README.routemaster_client.md).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor typo: but -> bus

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot, thank you!

include Rails.application.routes.url_helpers

def publish?
noop? || model.new_record? || model.previous_changes.any?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could always move this into the base implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds reasonable to me 👍

# app/publishers/order_publisher.rb
class OrderPublisher < BasePublisher
def url
api_order_url(model, host: ENV.fetch('API_HOST'), protocol: 'https')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could move this into the base implementation too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, no, this one can't really move into the base implementation because sometimes there's an api_ prefix on the routes, sometimes not, and sometimes it's different. That's why I just raised I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think this one is best to handle in the individual service apps indeed.

end

def roobus_url
ENV.fetch('ROOBUS_URL')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It strikes me that using ROUTEMASTER_URL and ROUTEMASTER_UUID would probably be cleaner names, but if people are dead set on ROOBUS_ then I guess that's fine.

Copy link
Contributor

@kplattret kplattret Aug 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just assumed that these were prefixed with ROOBUS_ for a good reason and didn't even question it, but it would definitely make sense to be more consistent and use ROUTEMASTER_. Happy to make this change if people agree.

Note: It looks like it would just leave ROOBUS_CALLBACK_URL using ROOBUS_.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd prefer ROUTEMASTER_ over ROOBUS_

Copy link
Contributor Author

@gregbeech gregbeech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM; couple of suggestions about docs that could be moved into the Publisher class as they look like they're sensible things to have in there anyway, but that could always be done at a later date.

@kplattret kplattret force-pushed the publish-lifecycle-events-to-routemaster branch from 147023b to 1c207a4 Compare August 3, 2017 13:19
README.md Outdated
- [HireFire Workers](#hirefire-workers)
- [Logging](#logging)
- [Google Oauth](#google-oauth)
- [Configuration and usage](#configuration-and-usage)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep the "library usage" v "command features" sections? Users have been confused by the dual nature of roo_on_rails (it's both a library/framework complement and a CLI, usable separately).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! I was a bit confused by this table of content myself, partly because it changed while I was working on this PR. I'll rename "Configuration and usage" to "Library usage" 👍

Copy link
Contributor

@Crunch09 Crunch09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

private

def bus_credentials_blank?
routemaster_url.blank? && routemaster_uuid.blank?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be routemaster_url.blank? || routemaster_uuid.blank? ? (Assuming both are mandatory)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes indeed, good spot, thank you.

@mezis mezis merged commit 30ef8f9 into master Aug 8, 2017
@kplattret kplattret deleted the publish-lifecycle-events-to-routemaster branch August 8, 2017 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants