-
Notifications
You must be signed in to change notification settings - Fork 474
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
raise ApiVersionNotSetError if Base.api_version is nil #605
Conversation
350afd5
to
b3b6a91
Compare
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.
Works for me. The other alternative would be to do this check in the caller? I'm not sure what's more appropriate for this gem.
I initially had the check in Looks like all the calls to api_version include:
|
Another alternative is moving over to a Null Object for no api version, so that it doesn't have an error like NoMethodError. Instead we can raise an exception that points to the gem has not been configured with an api version. This will remove the need to do a conditional check on nil on every use of api version in the gem. |
Ahhhh, forgot the NullObject pattern. Just updated. |
lib/shopify_api/api_version.rb
Outdated
@@ -109,5 +110,15 @@ def construct_graphql_path | |||
construct_api_path('graphql.json') | |||
end | |||
end | |||
|
|||
class NullVersion | |||
def self.nil? |
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.
Null object does not have to implement nil?
Instead implement a null version of all the methods on ApiVersion.
My suggestion would be
class NoVersionConfigured < StandardError; end
class NullVersion < ApiVersion
def initialize
@version_name = "no-version"
@numeric_version = 0
end
def stable?
false
end
def construct_api_path(path)
raise ApiVersionNotSetError, "You must set ShopifyAPI::Base.api_version before making a request."
end
def construct_graphql_path
raise ApiVersionNotSetError, "You must set ShopifyAPI::Base.api_version before making a request."
end
end
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.
If someone was checking to see if the version was nil, but it was actually NullVersion, I figured it fulfills the same meaning in that its not a version. Its just a fancier NilClass
I also think its probably misleading to have a null version respond to message other than raising an error. ShopifyAPI::Base.api_version.stable? # => false
implies there is a version set, its just not stable (:unstable perhaps). I did consider implementing every method and having it raise, but method_missing seemed like an easier choice.
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.
We could have it raise on all methods that matter. The main goal of the Null Object pattern is to remove nil checks and always have an implementation of the interface. Responding true
to the nil?
method would encourage the thing that we are trying to remove.
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.
The behavior we're trying to remove sending messages ApiVersion messages to NilObject where we expect an instance of ApiVersion and raising a no_method error. We don't want to provide any default behavior, we just want to replace any methods that ApiVersion has raise with a more meaningful message and avoid adding conditionals to handle the NoMethodError.
If we don't redefine nil?
to true
(and in hindsight, empty? and present?), then those values default to making the object truthy. Developers will still check if ShopifyAPI::ApiVersion.api_version.nil?
but the code will instead now blowup at an expected location opposed to where they expected it to possibly be nil.
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.
So there are two different ways of conceptualizing the NullObject pattern: as a "better null", which just has nicer errors or other properties, or as a specific non-null instance of the object which happens to get used when you might otherwise return null. Without getting too into the weeds of OO design, either will work for this particular case, though the latter (as Alex has suggested) is usually more common and probably what I'd go with for convention.
550c0b6
to
6c637b5
Compare
d952b43
to
72ffa96
Compare
updated so NullVersion only defines |
72ffa96
to
7d7ae85
Compare
Currently if you try to access a resource without setting an api_version first, you raise an exception:
Its not a super clear error. Instead, lets raise an error if the api_version hasn't been set (aka
ShopifyAPI::Base.api_version
would return nil) to let developers know they need to set the api version. This error is super easy to run into if you spin up the shopify_api_console and forget you need to set a version first.