Skip to content
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

Switched to Zeitwerk as autoloader. #16

Merged
merged 2 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### next

* TODO: Replace this bullet point with an actual description of a change.
* Switched to Zeitwerk as autoloader (#16)

### 1.4.0 (3 January 2025)

Expand Down
1 change: 1 addition & 0 deletions jabber_admin.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ Gem::Specification.new do |spec|

spec.add_dependency 'activesupport', '>= 6.1'
spec.add_dependency 'rest-client', '~> 2.1'
spec.add_dependency 'zeitwerk', '~> 2.6'
end
187 changes: 101 additions & 86 deletions lib/jabber_admin.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
# frozen_string_literal: true

require 'zeitwerk'
require 'active_support/inflector'
require 'json'
require 'pathname'
require 'rest-client'

require 'jabber_admin/exceptions'
require 'jabber_admin/configuration'
require 'jabber_admin/commands'
require 'jabber_admin/api_call'
require 'jabber_admin/version'

# jabber_admin
#
# This gem allows making API calls to the ejabberd RESTful admin backend. We
Expand Down Expand Up @@ -53,96 +48,116 @@
# @example Delete a user from the XMPP service, in fire and forget manner
# JabberAdmin.unregister user: '[email protected]'
module JabberAdmin
# Configure the relative gem code base location
root_path = Pathname.new("#{__dir__}/jabber_admin")

# Setup a Zeitwerk autoloader instance and configure it
loader = Zeitwerk::Loader.for_gem

# Do not auto load some parts of the gem
loader.ignore(root_path.join('errors.rb'))

# Finish the auto loader configuration
loader.setup

# Load standalone code
require 'jabber_admin/version'
require 'jabber_admin/errors'

# Make sure to eager load all constants
loader.eager_load

class << self
attr_writer :configuration
end

# A simple getter to the global JabberAdmin configuration structure.
#
# @return [JabberAdmin::Configuration] the global JabberAdmin configuration
def self.configuration
@configuration ||= Configuration.new
end
# A simple getter to the global JabberAdmin configuration structure.
#
# @return [JabberAdmin::Configuration] the global JabberAdmin configuration
def configuration
@configuration ||= Configuration.new
end

# Class method to set and change the global configuration. This is just a
# tapped variant of the +.configuration+ method.
#
# @yield [configuration]
# @yieldparam [JabberAdmin::Configuration] configuration
def self.configure
yield(configuration)
end
# Class method to set and change the global configuration. This is just a
# tapped variant of the +.configuration+ method.
#
# @yield [configuration]
# @yieldparam [JabberAdmin::Configuration] configuration
def configure
yield(configuration)
end

# Allow an easy to use DSL on the +JabberAdmin+ module. We support predefined
# (known) commands and unknown ones in bang and non-bang variants. This
# allows maximum flexibility to the user. The bang versions perform the
# response checks and raise in case of issues. The non-bang versions skip
# this checks. For unknown commands the +JabberAdmin::ApiCall+ is directly
# utilized with the method name as command. (Without the trailling bang, when
# it is present)
#
# @param method [Symbol, String, #to_s] the name of the command to run
# @param args [Array<Mixed>] all additional API call payload
# @param kwargs [Hash{Symbol => Mixed}] all additional API call payload
# @return [RestClient::Response] the actual response of the command
def self.method_missing(method, *args, **kwargs)
predefined_command(method).call(
predefined_callable(method), *args, **kwargs
)
rescue NameError
predefined_callable(method).call(method.to_s.chomp('!'), *args, **kwargs)
end
# Allow an easy to use DSL on the +JabberAdmin+ module. We support
# predefined (known) commands and unknown ones in bang and non-bang
# variants. This allows maximum flexibility to the user. The bang versions
# perform the response checks and raise in case of issues. The non-bang
# versions skip this checks. For unknown commands the
# +JabberAdmin::ApiCall+ is directly utilized with the method name as
# command. (Without the trailling bang, when it is present)
#
# @param method [Symbol, String, #to_s] the name of the command to run
# @param args [Array<Mixed>] all additional API call payload
# @param kwargs [Hash{Symbol => Mixed}] all additional API call payload
# @return [RestClient::Response] the actual response of the command
def method_missing(method, *args, **kwargs)
predefined_command(method).call(
predefined_callable(method), *args, **kwargs
)
rescue NameError
predefined_callable(method).call(method.to_s.chomp('!'), *args, **kwargs)
end

# Try to find the given name as a predefined command. When there is no such
# predefined command, we raise a +NameError+.
#
# @param name [Symbol, String, #to_s] the command name to lookup
# @return [Class] the predefined command class constant
def self.predefined_command(name)
# Remove bangs and build the camel case variant
"JabberAdmin::Commands::#{name.to_s.chomp('!').camelize}".constantize
end
# Try to find the given name as a predefined command. When there is no such
# predefined command, we raise a +NameError+.
#
# @param name [Symbol, String, #to_s] the command name to lookup
# @return [Class] the predefined command class constant
def predefined_command(name)
# Remove bangs and build the camel case variant
"JabberAdmin::Commands::#{name.to_s.chomp('!').camelize}".constantize
end

# Generate a matching API call wrapper for the given command name. When we
# have to deal with a bang version, we pass the bang down to the API call
# instance. Otherwise we just run the regular +#perform+ method on the API
# call instance.
#
# @param name [Symbol, String, #to_s] the command name to match
# @return [Proc] the API call wrapper
def self.predefined_callable(name)
method = name.to_s.end_with?('!') ? 'perform!' : 'perform'
proc do |*args, **kwargs|
if kwargs.empty?
ApiCall.send(method, *args)
else
ApiCall.send(method, *args, **kwargs)
# Generate a matching API call wrapper for the given command name. When we
# have to deal with a bang version, we pass the bang down to the API call
# instance. Otherwise we just run the regular +#perform+ method on the API
# call instance.
#
# @param name [Symbol, String, #to_s] the command name to match
# @return [Proc] the API call wrapper
def predefined_callable(name)
method = name.to_s.end_with?('!') ? 'perform!' : 'perform'
proc do |*args, **kwargs|
if kwargs.empty?
ApiCall.send(method, *args)
else
ApiCall.send(method, *args, **kwargs)
end
end
end
end

# Determine if a room exists. This is a convenience method for the
# +JabberAdmin::Commands::GetRoomAffiliations+ command, which can be used
# to reliably determine whether a room exists or not.
#
# @param room [String] the name of the room to check
# @return [Boolean] whether the room exists or not
def self.room_exist?(room)
get_room_affiliations!(room: room)
true
rescue JabberAdmin::CommandError => e
raise e unless /room does not exist/.match? e.response.body

false
end
# Determine if a room exists. This is a convenience method for the
# +JabberAdmin::Commands::GetRoomAffiliations+ command, which can be used
# to reliably determine whether a room exists or not.
#
# @param room [String] the name of the room to check
# @return [Boolean] whether the room exists or not
def room_exist?(room)
get_room_affiliations!(room: room)
true
rescue JabberAdmin::CommandError => e
raise e unless /room does not exist/.match? e.response.body

# We support all methods if you ask for. This is our dynamic command approach
# here to support predefined and custom commands in the same namespace.
#
# @param method [String] the method to lookup
# @param include_private [Boolean] allow the lookup of private methods
# @return [Boolean] always +true+
def self.respond_to_missing?(_method, _include_private = false)
true
false
end

# We support all methods if you ask for. This is our dynamic command
# approach here to support predefined and custom commands in the same
# namespace.
#
# @param method [String] the method to lookup
# @param include_private [Boolean] allow the lookup of private methods
# @return [Boolean] always +true+
def respond_to_missing?(_method, _include_private = false)
true
end
end
end
11 changes: 0 additions & 11 deletions lib/jabber_admin/commands.rb

This file was deleted.

File renamed without changes.