Skip to content

Instantly share code, notes, and snippets.

@fran-worley
Last active November 9, 2017 17:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fran-worley/5734768cc5b4a0160c69d2d60f7ed3c7 to your computer and use it in GitHub Desktop.
Save fran-worley/5734768cc5b4a0160c69d2d60f7ed3c7 to your computer and use it in GitHub Desktop.
require 'reform/form/dry'
require 'reform/form/coercion'
class AddressForm < Reform::Form
feature Reform::Form::Dry
feature Reform::Form::Coercion
property :is_digital, type: Types::Form::Bool, default: true
properties :address1, :address2, :city, :state, :zipcode, :phone, nilify: true # replaces '' with nil
validation do
configure do
# If using the latest reform you could define a base schema
# and then you wouldn't need to configure message location each time.
config.messages_file = 'config/locales/reform_messages.en.yml'
option :postcodes, ::TwitterCldr::Shared::PostalCodes # I like defining dependencies like this for clarity, totally optional
option :form # if using the latest reform replace this with { with: {form: true} } in the validation block
option :easypost, ::EasyPost::Address # I like defining dependencies like this for clarity, totally optional
def valid_zipcode?(zipcode)
postcodes.for_territory(:us).valid?(zipcode)
end
# from memory, predicates require one param even if you don't use it
def easypost_validator?(is_digital)
address = {
street1: form.address1,
street2: form.address2,
city: form.city,
state: form.state,
zip: form.zipcode
}
result = true
begin
easypost.create_and_verify(address)
rescue
result = false
end
Rails.logger.info("EasyPost address validation result for address #{address} result: #{result}")
result
end
end
required(:address1).maybe(:str?) # For explicitness I always like to define a type here, even for strings!
required(:address2).maybe(:str?) # in Reform, the key will always exist so 'optional' is pointless
required(:city).maybe(:str?)
required(:state).maybe(:str?) # if this is an int then you should use :int? here (you might want a custom message)
required(:zipcode).maybe(:str?, :valid_zipcode?)
required(:phone).maybe(:str?, size?: 10) # Your `phone=()` method should have removed the non-digits already
# if nils are possible then you'll may need to change this as (from memory) bool assumes true or false
required(:is_digital).filled(:bool?)
rule(address1: [:is_digital, :address1]) do |is_digital, address1|
is_digital.false?.then(address1.filled?)
end
rule(city: [:is_digital, :city]) do |is_digital, city|
is_digital.false?.then(city.filled?)
end
rule(zipcode: [:is_digital, :zipcode]) do |is_digital, zipcode|
is_digital.false?.then(zipcode.filled?)
end
rule(phone: [:is_digital, :phone]) do |is_digital, phone|
is_digital.false?.then(phone.filled?)
end
# To guard against errors in dependent properties be sure to declare all dependent properties
# as you only want this check to run when all their basic checks pass.
rule(easypost_validation: [
:is_digital, :address1, :address2, :city, :state, :zipcode
]) do |is_digital, address1, address2, city, state, zipcode| # you probably only need to declare is_digital here.
is_digital.false?.then(is_digital.easypost_validator?)
end
end
def phone
value = super.gsub(/\D/, '')
"(#{value[0..2]}) #{value[3..5]}-#{value[6..9]}"
end
def phone=(value)
super(value.gsub(/\D/, ''))
end
def state=(state)
return if state.empty?
super(Spree::State.find(state.to_i))
end
def zipcode
return super if super.length == 5
value = super.gsub(/\D/, '')
"#{value[0..4]}-#{value[5..9]}"
end
end
module GiftCardForms
class CheckoutForm < AddressForm
property :message
property :is_digital, default: false, inherit: true # override the AddressForm where the default is true
validation inherit: true do # append this rule to the existing validations in the default block
required(:message).filled(:str?)
end
end
end
@fran-worley
Copy link
Author

This assumes that you always validate that address fields are filled, but only run them through your easypost_validator? if is_digital == true.

There are ways to do this in pure dry-validation, but I personally find it's sometimes easier to embrace Reform sometimes!

I've not actually tested this but it should give you an idea of how you could implement this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment