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
Hashie::Extensions::IgnoreRequired mixin for ignoring required constraints #235
base: master
Are you sure you want to change the base?
Conversation
# IgnoreRequired is a simple mixin that silently ignores | ||
# required properties on initialization and assignment instead of | ||
# raising an error. This is useful when using a building an object | ||
# that will eventually be match a Dash but is temporarily incomplete. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"be match" is probably a typo :)
This is interesting. It undoes what a parent class mixes in, which is to enable these required validations. My first question was "why does someone need this?", then I read the description in #229. I am 50/50 on this. Why wouldn't you work with a Mash or a hash until the object is complete? |
I didn't notice your comment until now, but to answer your question it's because I want to ensure that the data is appropriate as it is entered, just not necessarily complete. Let's say for example you have this: class Coordinate < Hashie::Dash
include Hashie::Extensions::Coercion
property :latitude, required: true
coerce_key :latitude, ->(v) { Float(v) }
property :longitude, required: true
coerce_key :longitude, ->(v) { Float(v) }
end
class PartialCoordinate < Coordinate
include Hashie::Extensions::IgnoreRequired
end
# Cannot create a partial Coordinate
Coordinate.new(latitude: 40.712784)
# ArgumentError: The property 'longitude' is required for Coordinate.
# So create a PartialCoordinate instead...
latlong = PartialCoordinate.new(latitude: 40.712784)
# => {:latitude=>40.712784}
# Unlike a Mash or Hash, you'll still get errors if you use a bad key
latlong.long = -74.005941
# NoMethodError: undefined method `long=' for #<PartialCoordinate latitude=40.712784>
# Or if you try to set invalid data
latlong.longitude = "74° 0' 21.3876\" W"
# ArgumentError: invalid value for Float(): "74\xC2\xB0 0' 21.3876\" W"
# If you try to coerce it to a full coordinate you'll get an error if it's incomplete
Coordinate.new(latlong)
# ArgumentError: The property 'longitude' is required for Coordinate.
# But once it's complete...
latlong.longitude = -74.005941
Coordinate.new(latlong)
# => {:latitude=>40.712784, :longitude=>-74.005941} Another thing about this I really like is that data errors are raised at the line where the data is set rather than where it's converted. If you have code like this: data = Hashie::Mash.new
data.latitude = 40.712784
# This is the line where I *want* the error to be raised
data.longitude = "74° 0' 21.3876\" W"
# This is the line where the error is actually raised...
coord = Coordinate.new(data) That can be particularly useful in long methods or methods with a lot of branches. So I think there's clear advantages of something like this over a Hash/Mash, but I'm not sure this is the implementation I want. I agree w/ you that I'd rather have mixins that add behavior that remove it. |
I see, and I am now 80/20 :) I'd like to hear some other people's opinions about this, but in the meantime you should fix the specs, squash, update CHANGELOG, README & al. I think I would merge this. |
Ha - I'm 20/80 now ;) I still want to be able to ignore required constraints, but I think this is a hacky way to do it. I'm thinking about other problems as well, like being able to skipping coercion for rspec instance_doubles, and supporting enums or other validations (#182). So I think I'd rather see a proper validate/validator API first. This might still be the solution, but the code could turn into something more readable like: def included(base)
base.hashie_validators.each do | validator |
validator.disable if validator === Hashie::Validator::Required
end
end Or an API might even make something like this possible: Hashie.without_validating(Hashie::Validator::Required) do
latlong = Coordinate.new
latlong.latitude = 40.712784
latlong.longitude = -74.005941
latlong
end.validate The second approach would actually work better for the Bash / "Builder Hash" proposal, assuming it (temporarily) disables the validator on nested objects as well. |
I think to move forward:
|
377fe9b
to
503697d
Compare
I really like the |
I thought about Do you know an appropriate way to store the state? I'm planning to use |
I think |
I just noticed
Hashie::Extensions::IgnoreUndeclared
and thought that a similar mixin could be useful to implement a more maintainableHashie::Bash
like I proposed in #229.This PR implements
Hashie::Extensions::IgnoreRequired
which is similar to IgnoreUndeclared and could be an important building-block of a futureHashie::Bash
implementation. This mixin disables required constraints, which was a main feature of theHashie::Bash
, but it does not handle nested structures or have a fluent API for assignment like the original proposal in #229.