lib/cistern/client.rb
# frozen_string_literal: true
module Cistern::Client
module Collections
def collections
cistern.collections
end
def requests
cistern.requests
end
end
# custom include
def self.with(options = {})
client_module = Module.new
custom_include = <<-EOS
def self.included(klass)
Cistern::Client.setup(klass, #{options.inspect})
super
end
EOS
client_module.class_eval(custom_include, __FILE__, __LINE__)
client_module
end
# vanilla include
def self.included(klass)
setup(klass)
super
end
def self.setup(klass, options = {})
request_class = options[:request] || 'Request'
collection_class = options[:collection] || 'Collection'
model_class = options[:model] || 'Model'
singular_class = options[:singular] || 'Singular'
interface = options[:interface] || :class
interface_callback = (:class == interface) ? :inherited : :included
if interface == :class
Cistern.deprecation(
%q{'class' interface is deprecated. Use `include Cistern::Client.with(interface: :module). See https://github.com/lanej/cistern#custom-architecture},
caller[2],
)
end
unless klass.name
fail ArgumentError, "can't turn anonymous class into a Cistern cistern"
end
klass.class_eval <<-EOS, __FILE__, __LINE__
module Collections
include ::Cistern::Client::Collections
def cistern
Cistern.deprecation(
'#cistern is deprecated. Please use #cistern',
caller[0]
)
#{klass.name}
end
def cistern
#{klass.name}
end
end
def self.cistern
Cistern.deprecation(
'#cistern is deprecated. Please use #cistern',
caller[0]
)
#{klass.name}
end
def self.cistern
#{klass.name}
end
class Real
def initialize(options={})
end
def mocking?
false
end
end
class Mock
def initialize(options={})
end
def mocking?
true
end
end
#{interface} #{model_class}
def self.#{interface_callback}(klass)
cistern.models << klass
klass.send(:include, ::Cistern::Model)
super
end
def self.cistern
Cistern.deprecation(
'#cistern is deprecated. Please use #cistern',
caller[0]
)
#{klass.name}
end
def self.cistern
#{klass.name}
end
end
#{interface} #{singular_class}
def self.#{interface_callback}(klass)
cistern.singularities << klass
klass.send(:include, ::Cistern::Singular)
super
end
def self.service
Cistern.deprecation(
'#service is deprecated. Please use #cistern',
caller[0]
)
#{klass.name}
end
def self.cistern
#{klass.name}
end
end
#{interface} #{collection_class}
include ::Cistern::Collection
def self.#{interface_callback}(klass)
klass.send(:extend, Cistern::Attributes::ClassMethods)
klass.send(:extend, Cistern::Collection::ClassMethods)
klass.send(:include, Cistern::Attributes::InstanceMethods)
cistern.collections << klass
super
end
def self.service
Cistern.deprecation(
'#service is deprecated. Please use #cistern',
caller[0]
)
#{klass.name}
end
def self.cistern
#{klass.name}
end
end
#{interface} #{request_class}
include ::Cistern::Request
def self.service
Cistern.deprecation(
'#service is deprecated. Please use #cistern',
caller[0]
)
#{klass.name}
end
def self.cistern
#{klass.name}
end
def self.#{interface_callback}(klass)
klass.extend(::Cistern::Request::ClassMethods)
cistern.requests << klass
super
end
end
EOS
klass.send(:extend, Cistern::Client::ClassMethods)
klass.send(:const_set, :Timeout, Class.new(Cistern::Error))
klass::Mock.send(:include, klass::Collections)
klass::Mock.send(:extend, Cistern::WaitFor)
klass::Mock.timeout_error = klass::Timeout
klass::Mock.send(:extend, Cistern::Data)
klass::Real.send(:include, klass::Collections)
klass::Real.send(:extend, Cistern::WaitFor)
klass::Real.timeout_error = klass::Timeout
end
module ClassMethods
def mock!
@mocking = true
end
def mocking?
@mocking
end
def unmock!
@mocking = false
end
def collections
@collections ||= []
end
def models
@_models ||= []
end
def singularities
@_singularities ||= []
end
def recognized_arguments
@_recognized_arguments ||= []
end
def required_arguments
@_required_arguments ||= []
end
def requests
@_requests ||= []
end
def requires(*args, **kwargs)
required_arguments.concat(args)
end
def recognizes(*args, **kwargs)
recognized_arguments.concat(args)
end
def validate_options(options = {})
required_options = Cistern::Hash.slice(options, *required_arguments)
missing_required_options = required_arguments - required_options.keys
unless missing_required_options.empty?
fail "Missing required options: #{missing_required_options.inspect}"
end
unrecognized_options = options.keys - (required_arguments + recognized_arguments)
unless unrecognized_options.empty?
fail "Unrecognized options: #{unrecognized_options.inspect}"
end
end
def setup
return true if @_setup
requests.each do |klass|
name = klass.cistern_method ||
Cistern::String.camelize(Cistern::String.demodulize(klass.name))
Cistern::Request.cistern_request(self, klass, name)
end
collections.each do |klass|
name = klass.cistern_method ||
Cistern::String.underscore(klass.name.gsub("#{self.name}::", '').gsub('::', ''))
Cistern::Collection.cistern_collection(self, klass, name)
end
models.each do |klass|
name = klass.cistern_method ||
Cistern::String.underscore(klass.name.gsub("#{self.name}::", '').gsub('::', ''))
Cistern::Model.cistern_model(self, klass, name)
end
singularities.each do |klass|
name = klass.cistern_method ||
Cistern::String.underscore(klass.name.gsub("#{self.name}::", '').gsub('::', ''))
Cistern::Singular.cistern_singular(self, klass, name)
end
@_setup = true
end
def new(options = {})
setup
validate_options(options)
const_get(self.mocking? ? :Mock : :Real).new(options)
end
def reset!
const_get(:Mock).reset!
end
end
end