Skip to content

Instantly share code, notes, and snippets.

@fsubal
Last active January 25, 2025 15:40
Show Gist options
  • Save fsubal/b53d00b6c74e5951c8c09c8f8c74dc5c to your computer and use it in GitHub Desktop.
Save fsubal/b53d00b6c74e5951c8c09c8f8c74dc5c to your computer and use it in GitHub Desktop.
module AttrReaderGuard
extend ActiveSupport::Concern
class InvalidError < ArgumentError
PREFIX = '[attr_reader_guard!]:'
def initialize(subject, predicate)
super [PREFIX, subject, predicate].join(' ')
end
end
included do
cattr_reader :attr_reader_guards, default: ->{ {} }
end
class_methods do
def attr_reader_guard!(name, &matcher)
unless block_given?
raise InvalidError, name, 'has no matcher.'
end
@attr_reader_guards ||= {}
@attr_reader_guards[name.to_sym] = matcher
attr_reader name.to_sym
end
end
def initialize(**attributes)
super(**attributes)
attributes.each do |name, value|
matcher = attr_reader_guards.fetch(name.to_sym)
unless matcher.call(value)
raise InvalidError, name, 'does not satisfy the pattern'
end
instance_variable_set("@#{name}", value)
rescue KeyError
raise InvalidError, name, "is not accepted for #{self.class} (unknown attribute)"
end
end
end
class ResizeImages
include AttrReaderGuard
attr_reader_guard!(:width) { it in Integer | /^\d+$/ }
attr_reader_guard!(:height) { it in Integer | /^\d+$/ }
attr_reader_guard!(:input_images) { it in [String, String, String] }
attr_reader_guard!(:object_fit) { it in 'cover' | 'contain' }
def width = super.to_i
def height = super.to_i
def header_image = input_images.first
def body_image = input_images.second
def footer_image = input_images.third
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment