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

Reload hooks when metadata changes #1089

Closed
wants to merge 9 commits into from

Conversation

JonRowe
Copy link
Member

@JonRowe JonRowe commented Sep 30, 2013

This is a trivial take at solving rspec/rspec-rails#829 but simply watching for metadata changes
and then applying hooks again when they change. Hooks shouldn't be included twice due to the
checks in the existing call.

Simply moving the call to register the hooks didn't work due to ordering on the hooks.

Thoughts? /cc @myronmarston

JonRowe added a commit to rspec/rspec-rails that referenced this pull request Sep 30, 2013
@myronmarston
Copy link
Member

I think I'd rather see the hook registration delayed until they are actually needed, as I suggested in the rspec-rails issue:

One idea is to move the hooks.register_globals call out of set_it_up and into run, so that the hooks are registered at the last possible moment.

Any reason that won't work? Seems far less complex, and would perform better.

@JonRowe
Copy link
Member Author

JonRowe commented Sep 30, 2013

It completely buggers the ordering, this is actually a far simpler change.

@myronmarston
Copy link
Member

It completely buggers the ordering, this is actually a far simpler change.

Can you expand on that a bit? I'm not following...

@JonRowe
Copy link
Member Author

JonRowe commented Sep 30, 2013

Moving the call to run means that the hooks from the configuration will run after local hooks (not desirable).

So I attempted to solve this by changing how the hooks are added to the array but then local hooks are just intermingled with the configuration hooks, so I moved the registration call to the hook methods so it's a "JIT" registration of hooks, however this messes with the ordering of hooks in nested contexts.

@JonRowe
Copy link
Member Author

JonRowe commented Sep 30, 2013

So at this point the best place to load the configuration hooks is actually in the existing place, and then load in any extra hooks on metadata changes, or rewrite the hook system so we have more control over the ordering. The later is more complexity and more overhead.

def []=(key, value)
super
@callback.call if defined?(@callback)
end
Copy link
Member

Choose a reason for hiding this comment

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

There are other ways to change an metadata hash, right? Such as store, or any of the xyz! methods. Should the on_change hook be triggered for them?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the only one we use AFAIK but yes they should all follow this pattern, I'll go over the enum spec and add them all.

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've updated this for the ones that set metadata, this doesn't deregister hooks at the moment (that would require more changes, do we want to support that?)

Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to deregister hooks. on_change is private, anyway.

I've updated this for the ones that set metadata,

What about delete, delete_if, select! and reject!?

Copy link
Member Author

Choose a reason for hiding this comment

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

They don't set metadata, but remove it, hence my comment

this doesn't deregister hooks at the moment (that would require more changes, do we want to support that?

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'm ok with that too, or even just publicly stating we only support adding to metadata. If we stopped inheriting from hash and delegating to an internal hash we could control the api and reject those methods totally.

Copy link
Member

Choose a reason for hiding this comment

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

It'd be good to merge this PR soon, but I'd still like others to weight in (I'm still very much on the fence). @xaviershay? @samphippen? @soulcutter?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ping?

Copy link
Member

Choose a reason for hiding this comment

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

I'm OK stating that it's not intended to be a public API but is just intended for use by rspec-rails and is subject to change in a minor release.

This. We should collect stronger use-cases before exposing this publicly.

Copy link
Member

Choose a reason for hiding this comment

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

Reading this more, seems like we're between a rock and a hard place. We've exposed a full Hash as our interface, so are at the mercy of future versions of ruby to keep the method signatures that we're overriding for dirty tracking the same.

Seems like since we already have an iteration over methods in the latest version, it would be straight forward to just add the extra methods there.

@myronmarston
Copy link
Member

Thanks, @JonRowe, after hearing you explain that this definitely seems like the right direction. Left a few comments.

@JonRowe
Copy link
Member Author

JonRowe commented Oct 1, 2013

@myronmarston I've addressed the issues commented about

@myronmarston
Copy link
Member

Looks good...seems like you are missing a few mutation methods, though (see above).


[:[]=, :store, :merge!].each do |name|
define_method(name) do |*args|
super(*args)
Copy link
Member

Choose a reason for hiding this comment

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

merge! accepts a block:

http://www.ruby-doc.org/core-2.0.0/Hash.html#method-i-merge-21

I think that it gets forwarded automatically via super, but it would be good to spec that.

@JonRowe
Copy link
Member Author

JonRowe commented Oct 1, 2013

Looks good...seems like you are missing a few mutation methods, though (see above).

The other mutation methods only remove things from the hash, there's currently nothing to remove hooks when you remove metadata anyway. I left off the other mutation methods to make this more apparent. (See above)

@xaviershay xaviershay mentioned this pull request Nov 2, 2013
13 tasks
@@ -156,6 +156,19 @@ def ascending_numbers
expect(examples_run.count).to eq(1)
end

it "still runs matching config hooks when metadta is added later" do
Copy link
Member

Choose a reason for hiding this comment

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

s/metadta/metadata/

@xaviershay
Copy link
Member

Rebased and added extra mutation methods. Note that overriding update doesn't work like the other ones (totally breaks), haven't figured out why yet.

@xaviershay
Copy link
Member

oop, forgot about compat for old versions again :/

@xaviershay
Copy link
Member

Given that this is not a new feature, and that Jon is away all weekend, I'm going to argue that this should not be a blocker for 3.0.0 pre. Any objections?

@JonRowe
Copy link
Member Author

JonRowe commented Nov 4, 2013

Thanks for adding the extra mutation methods @xaviershay, any ideas on how to actually make this work in those situations? (e.g. actually remove now unnecessary hooks?)

@xaviershay
Copy link
Member

Ah I see, I didn't understand the problem. Going to think about this after 3-pre. If you wanted to mark as private and merge without the extra mutation methods I'd be ok with that.

@JonRowe
Copy link
Member Author

JonRowe commented Nov 6, 2013

WDYT @myronmarston? Merge without the mutation methods? Or at least with the mutation methods marked as private...?

@myronmarston
Copy link
Member

IIRC, this is fixing a bug in rspec-rails 2.14, right? Let's merge this in some form ASAP. I'm not sure what "with the mutation methods marks as private" means -- the metadata object is a hash, and the mutation methods are part of a hash's public interface. What do you mean by that?

@JonRowe
Copy link
Member Author

JonRowe commented Nov 6, 2013

Following on from @xaviershay's suggestion, they're public for Hash we don't have to make them public for MetaData

@myronmarston
Copy link
Member

Following on from @xaviershay's suggestion, they're public for Hash we don't have to make them public for MetaData

Hmmm...I don't think we can do that in RSpec 2.x -- it would violate semver. We can do it in RSpec 3, but I'm wondering if there's a better approach we can do in 3.0? Can we change how rspec-rails does it's inclusion (maybe along the lines of rspec/rspec-rails#662) so that this is unnecessary?

@JonRowe
Copy link
Member Author

JonRowe commented Nov 14, 2013

@myronmarston However we solve rspec-rails (I still think this is the easiest way) we should either fix this or make metadata immutable. As it stands it's confusing IMO.

@JonRowe JonRowe mentioned this pull request Mar 2, 2014
@myronmarston
Copy link
Member

@JonRowe -- clearly this solution isn't going to work with the metadata hash just being a raw hash...we should figure out an alternate solution. Keeping open for now until we figure that out.

@JonRowe
Copy link
Member Author

JonRowe commented Mar 15, 2014

I think we need an api to mutate the hash, that triggers the hook inclusion et al, then rspec-rails can call that instead

@myronmarston
Copy link
Member

I think we need an api to mutate the hash, that triggers the hook inclusion et al, then rspec-rails can call that instead

What kind of API do you have in mind? I'm having trouble coming up with a non-clunky API for that.

Here's an alternate idea I presented in #1231:

RSpec.configure do |rspec|
  rspec.define_inferred_metadata do |metadata|
    metadata[:type] = type_for(metadata[:file_path])
  end
end

Basically, rather than officially supporting all metadata features when you mutate metadata after the fact, provide an API to allow inferred metadata entries (which rspec-rails currently mutates the metadata to achieve), and rspec-core will ensure the inferred entries are set before it does anything with the metadata.

@JonRowe
Copy link
Member Author

JonRowe commented Mar 15, 2014

Here's an alternate idea I presented in #1231:

👍

@myronmarston
Copy link
Member

@JonRowe -- whatever we come up with should address #969 as well.

@myronmarston
Copy link
Member

Closing in favor of #1496

@JonRowe JonRowe deleted the reload_hooks_on_metadata_change branch April 21, 2014 22:19
myronmarston added a commit that referenced this pull request Apr 22, 2014
myronmarston added a commit that referenced this pull request Dec 17, 2014
Fully supporting metadata mutation is fraught with difficulties
see (rspec/rspec-rails#829 and the attempted fix in #1089).
For RSpec 3, we decided to expose `define_derived_metadata`
as an officially supported way to accomplish the sorts of
things metadata mutation was used for in RSpec 2. This spec
got left behind (since it was passing at the time, it wasn't
noticed) but isn’t something we want to support going forward
since module inclusion shouldn't mutate metadata. It’s also
getting in the way of a perf optimization I'm implementing.
myronmarston added a commit that referenced this pull request Dec 18, 2014
Fully supporting metadata mutation is fraught with difficulties
see (rspec/rspec-rails#829 and the attempted fix in #1089).
For RSpec 3, we decided to expose `define_derived_metadata`
as an officially supported way to accomplish the sorts of
things metadata mutation was used for in RSpec 2. This spec
got left behind (since it was passing at the time, it wasn't
noticed) but isn’t something we want to support going forward
since module inclusion shouldn't mutate metadata. It’s also
getting in the way of a perf optimization I'm implementing.
myronmarston added a commit that referenced this pull request Dec 19, 2014
Fully supporting metadata mutation is fraught with difficulties
see (rspec/rspec-rails#829 and the attempted fix in #1089).
For RSpec 3, we decided to expose `define_derived_metadata`
as an officially supported way to accomplish the sorts of
things metadata mutation was used for in RSpec 2. This spec
got left behind (since it was passing at the time, it wasn't
noticed) but isn’t something we want to support going forward
since module inclusion shouldn't mutate metadata. It’s also
getting in the way of a perf optimization I'm implementing.
MatheusRich pushed a commit to MatheusRich/rspec-core that referenced this pull request Oct 30, 2020
Fully supporting metadata mutation is fraught with difficulties
see (rspec/rspec-rails#829 and the attempted fix in rspec#1089).
For RSpec 3, we decided to expose `define_derived_metadata`
as an officially supported way to accomplish the sorts of
things metadata mutation was used for in RSpec 2. This spec
got left behind (since it was passing at the time, it wasn't
noticed) but isn’t something we want to support going forward
since module inclusion shouldn't mutate metadata. It’s also
getting in the way of a perf optimization I'm implementing.
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