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

WIP: Add ignoring_interference_by_writer to everything #866

Closed
wants to merge 5 commits into from

Conversation

mcmire
Copy link
Collaborator

@mcmire mcmire commented Dec 18, 2015

This is basically an answer to #799, #800, #819, and other issues, and also supercedes #820.

The idea here is to add ignoring_interference_by_writer to all matchers (except for allow_value) -- but with a key twist, which is that matchers are still aware of when attributes change values, but it will only let the user know about them if the matcher in question fails. This way, the user (and I) still have a window in what is going on under the hood, but it won't disrupt what the user is trying to do normally.

it 'accepts (and not raise an error)' do
model = define_model_validating_uniqueness(
attribute_name: :name,
validation_options: { case_sensitive: false }

Choose a reason for hiding this comment

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

Put a comma after the last parameter of a multiline method call.

@mcmire mcmire added this to the 3.0.2 milestone Dec 18, 2015
@mcmire mcmire force-pushed the ew-add-ignoring-interference-by-writer branch 2 times, most recently from 24a358e to f6b62bb Compare December 23, 2015 07:53
@@ -14,6 +14,8 @@
config.infer_spec_type_from_file_location!
end

config.example_status_persistence_file_path = "spec/examples.txt"

Choose a reason for hiding this comment

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

Prefer single-quoted strings when you don't need string interpolation or special symbols.


config.infer_spec_type_from_file_location!
config.example_status_persistence_file_path = "spec/examples.txt"
config.alias_it_behaves_like_to(:it_supports, "it supports")

Choose a reason for hiding this comment

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

Prefer single-quoted strings when you don't need string interpolation or special symbols.

def validation_matcher_scenario_args
{
matcher_name: :validate_uniqueness_of,
model_creator: :"active_record/uniqueness_matcher"

Choose a reason for hiding this comment

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

Put a comma after the last item of a multiline hash.

We need to use this in another part of the codebase: later, when we add
ignoring_interference_by_writer to all matchers, we will need a way of
overriding an attribute such that it produces another value that
invalidates the test. Some tests require us to generate dummy values.
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.

What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.

So, to help us with this, this commit takes the simple pattern of
model-plus-validation previously mentioned and places it in a class,
then splits up that logic. So there are actually four main classes that
we are introducing here:

* CreateModel: for creating a model in an abstract way. You'll couple
  this with one of two lower-level strategies, which may also be used on
  their own:
  * ModelCreationStrategies::ActiveModel
  * ModelCreationStrategies::ActiveRecord
* ActiveRecord::CreateTable: for creating a table that goes along with
  an ActiveRecord model.

Note that `define_model` and `define_active_model_class` have not been
changed so as not to completely break every single test, but they now
simply call out to ModelCreationStrategies::ActiveModel and
ModelCreationStrategies::ActiveRecord internally.
Also add a helper method, `build_scenario_for_validation_matcher`, to
all unit tests.

Our plan is to slowly transition all of the tests to use this helper
method, and that we we can get rid of the umpteen different ways we are
building records across all of the tests.
When used against an attribute that's an enum, `allow_value` will raise
an AttributeValueChangedError. This commit prevents that from happening.
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."

That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.

So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.

But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
@mcmire mcmire force-pushed the ew-add-ignoring-interference-by-writer branch from e016615 to e56a9cc Compare December 31, 2015 20:18
def validation_matcher_scenario_args
super.deep_merge(
matcher_name: :validate_uniqueness_of,
model_creator: :"active_record/uniqueness_matcher"

Choose a reason for hiding this comment

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

Put a comma after the last parameter of a multiline method call.

@mcmire mcmire closed this Jan 5, 2016
@mcmire mcmire deleted the ew-add-ignoring-interference-by-writer branch January 5, 2016 04:36
@mcmire
Copy link
Collaborator Author

mcmire commented Jan 6, 2016

The branch for this was removed too early, oops -- but this ended up being merged as 1189934 and 5532f43.

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.

2 participants