Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Allow the reporter to send custom events to registered formatters #1869

Merged
merged 2 commits into from
Feb 8, 2015

Conversation

JonRowe
Copy link
Member

@JonRowe JonRowe commented Feb 6, 2015

This is an extension of #1866 as part of work for #1851, it allows the reporter to send an arbitrary event to any registered listeners, it won't allow you to send RSpec internal events and creates a CustomNotification for each event. /cc @nyarly

@nyarly
Copy link

nyarly commented Feb 6, 2015

That's rad - thanks so much

:dump_profile, :dump_summary, :example_failed, :example_group_finished,
:example_group_started, :example_passed, :example_pending, :example_started,
:message, :seed, :start, :start_dump, :stop
]
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be a set rather than an array, so that NOTIFICATIONS.include? has constant-time complexity (given that's how you use it).

Copy link
Member

Choose a reason for hiding this comment

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

Also, when we add new lifecycle events in the future, it'll be easy to forget to add them to this list. Can you think of a way to add some kind of spec that enforces that all lifecycle events are in this list and prevents us from adding a new event w/o adding it here?

Copy link
Member

Choose a reason for hiding this comment

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

So I still think this should be a set for better perf, given that this isn't a super small array (e.g. <= 5 items).

Also, I'd still like something that enforces that we keep this up to date. One idea: make notify verify that the given event is included in this list, unless the caller of notify passes a :skip_supported_notification_verification option (or similar...that's kind a long option name). Then from publish it can pass that flag, but for all other notifications it would only be allowed if the event is in this list, which would enforce that this list is always kept up-to-date with all lifecycle events.

Also, should the constant be RESERVED_NOTIFICATIONS or LIFECYCLE_NOTIFICATIONS or something?

Copy link
Member Author

Choose a reason for hiding this comment

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

So I still think this should be a set for better perf, given that this isn't a super small array (e.g. <= 5 items).

I didn't want to use Set because it requires us to require it, but I've realised we already do for the deprecation formatter... I have mixed feelings about that, I know the performance is better but I'd really like to avoid requiring parts of the standard library, it's something that can catch us out.

Also, I'd still like something that enforces that we keep this up to date. One idea: make notify verify that the given event is included in this list, unless the caller of notify passes a :skip_supported_notification_verification option (or similar...that's kind a long option name). Then from publish it can pass that flag, but for all other notifications it would only be allowed if the event is in this list, which would enforce that this list is always kept up-to-date with all lifecycle events.

Personally I'd like to avoid the overhead of having to check each time in notify, but I'd be willing to work up a test to check that they're all included somehow?

Also, should the constant be RESERVED_NOTIFICATIONS or LIFECYCLE_NOTIFICATIONS or something?

I've changed it to RSPEC_NOTIFICATIONS if you think that helps?

Copy link
Member

Choose a reason for hiding this comment

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

I didn't want to use Set because it requires us to require it, but I've realised we already do for the deprecation formatter... I have mixed feelings about that, I know the performance is better but I'd really like to avoid requiring parts of the standard library, it's something that can catch us out.

Set is a simple wrapper around Hash which is what really gives constant time inclusion checks, so we can just use a Hash instead (e.g. with a value of true for each key or whatever...the values don't actually matter). We should probably look into doing that for our other uses of Set although that's out of scope for this PR IMO. We may also want to create our own wrapper class that wraps a Hash and provides the tiny bit of the Set interface we actually use.

Personally I'd like to avoid the overhead of having to check each time in notify, but I'd be willing to work up a test to check that they're all included somehow?

A test would be cool but I couldn't think of a way to test that because such a test would need a list of which events to test, but that list itself is what we are actually trying to test. I came up with this, though...it's basically the idea I mentioned before, but implemented in a prepended module that is defined in spec/support/formatter_support so as to only be active in test environments and not add any runtime overhead (besides to rspec-core's spec suite).

if RSpec::Support::RubyFeatures.module_prepends_supported?
  module RSpec::Core
    class Reporter
      module EnforceRSpecNotificationsListComplete
        def notify(event, *args)
          return super if caller_locations(1, 1).first.label =~ /publish/
          return super if RSPEC_NOTIFICATIONS.include?(event)

          raise "#{event.inspect} must be added to `RSPEC_NOTIFICATIONS`"
        end
      end

      prepend EnforceRSpecNotificationsListComplete
    end
  end
end

I've changed it to RSPEC_NOTIFICATIONS if you think that helps?

👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok I'll use a set for now and then work on replacing Set in a new PR after this.

I'm happy to use your module prepend idea.

@myronmarston
Copy link
Member

I like the idea and the implementation is nice and straightforward.

@JonRowe JonRowe force-pushed the allow_publishing_custom_events_via_reporter branch from 400bf72 to 6cc0b28 Compare February 6, 2015 23:37
@JonRowe
Copy link
Member Author

JonRowe commented Feb 6, 2015

Fixed up

@JonRowe JonRowe force-pushed the allow_publishing_custom_events_via_reporter branch from 6cc0b28 to a59268b Compare February 6, 2015 23:40
end

it 'will raise when encountering RSpec standard events' do
expect { reporter.publish :start }.to raise_error
Copy link
Member

Choose a reason for hiding this comment

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

raise_error with no args is prone to false positives -- for example, if you typo'd reporter or publish it would never execute the code you are intending to test but the spec would pass. I think we should always pass some arg to raise_error that specifies some aspect of the error that couldn't result from ruby raising a NameError or ArgumentError.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fair enough, I've modified this to check the error.

@myronmarston
Copy link
Member

Fixed up

The fixes are an improvement but there are a couple more things I'd like to see before this is merged.

@JonRowe JonRowe force-pushed the allow_publishing_custom_events_via_reporter branch 3 times, most recently from 14f172c to 54f9030 Compare February 8, 2015 00:15
UnsupportedPublish = StandardError.new(
"RSpec::Core::Reporter#publish is intended for sending custom " \
"events not internal RSpec ones, please rename your custom event."
)
Copy link
Member

Choose a reason for hiding this comment

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

This is kinda non-standard for how we've normally done errors. UnsupportedPublish looks like it's a class constant but it's not. Also, using a singleton instance is potentially problematic if the message gets mutated...

What advantage did you see to doing it this way?

BTW, I think it should use ArgumentError, not StandardError as it's for a bad argument, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

I can go back to a bare raise with a string if required, the thinking behind the singleton was just to save allocations.

Copy link
Member

Choose a reason for hiding this comment

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

I can go back to a bare raise with a string if required, the thinking behind the singleton was just to save allocations.

I'm not really concerned about excessive allocations here...it's going to be extremely rare that users hit this error.

@JonRowe JonRowe force-pushed the allow_publishing_custom_events_via_reporter branch from 54f9030 to b673cd7 Compare February 8, 2015 02:23
@JonRowe JonRowe force-pushed the allow_publishing_custom_events_via_reporter branch from b673cd7 to 4e906eb Compare February 8, 2015 02:37
@@ -1,7 +1,17 @@
require 'set'
module RSpec::Core
Copy link
Member

Choose a reason for hiding this comment

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

I think it looks weird to not have a blank line between the require and the module def, but since you'll be removing the set require in another PR soon feel free to merge with this as-is :).

@myronmarston
Copy link
Member

LGTM, merge when green.

JonRowe added a commit that referenced this pull request Feb 8, 2015
…a_reporter

Allow the reporter to send custom events to registered formatters
@JonRowe JonRowe merged commit 9d49bcb into master Feb 8, 2015
@JonRowe JonRowe deleted the allow_publishing_custom_events_via_reporter branch February 8, 2015 05:04
myronmarston added a commit that referenced this pull request Feb 9, 2015
@myronmarston
Copy link
Member

Noticed you didn't add a changelog entry for this so I added one in f00b298. Feel free to revise if you have a better description in mind :).

@JonRowe
Copy link
Member Author

JonRowe commented Feb 9, 2015

Sorry, I did this at RubyConAU so I may have gotten distracted ;)

JonRowe added a commit that referenced this pull request Feb 9, 2015
MatheusRich pushed a commit to MatheusRich/rspec-core that referenced this pull request Oct 30, 2020
…ts_via_reporter

Allow the reporter to send custom events to registered formatters
MatheusRich pushed a commit to MatheusRich/rspec-core that referenced this pull request Oct 30, 2020
MatheusRich pushed a commit to MatheusRich/rspec-core that referenced this pull request Oct 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants