module JamfZeitwerkConfig

Jamf’s Zeitwerk Config and processes

Constants

EAGER_LOAD_FILE

touch this file to make zeitwek eager-load everything when the gem is required.

VERBOSE_LOADING_ENV

Or, set this ENV var to also make zeitverk and mixins send text to stderr

VERBOSE_LOADING_FILE

touch this file to make zeitwerk and mixins send text to stderr as things load or get mixed in

Public Class Methods

eager_load_for_testing() click to toggle source

For testing the Zeitwrk Loader. Normally we want autoloading on demand, eager loading loads everything so we can see it

To make this happen touch the file defined in EAGER_LOAD_FILE

    # File lib/jamf/zeitwerk_config.rb
218 def self.eager_load_for_testing
219   return unless EAGER_LOAD_FILE.file?
220 
221   loader.eager_load(force: true)
222   warn :loaded
223   # rescue Zeitwerk::NameError => e
224   #   warn e.message
225 end
load_msg(msg) click to toggle source

rubocop: disable Style/StderrPuts

   # File lib/jamf/zeitwerk_config.rb
52 def self.load_msg(msg)
53   $stderr.puts msg if verbose_loading?
54 end
loader() click to toggle source

The loader object for Xolo

   # File lib/jamf/zeitwerk_config.rb
58 def self.loader
59   @loader
60 end
setup_zeitwerk_loader(zloader) click to toggle source

Configure the Zeitwerk loader, See github.com/fxn/zeitwerk

    # File lib/jamf/zeitwerk_config.rb
 63 def self.setup_zeitwerk_loader(zloader)
 64   @loader = zloader
 65 
 66   # Ignore this file (more ignores below)
 67   loader.ignore __FILE__
 68 
 69   ##### Collaped Paths
 70 
 71   # these paths all define classes & modules directly below 'Jamf'
 72   # If we didn't collapse them, then e.g.
 73   #   /jamf/api/base_classes/classic/group.rb
 74   # would be expected to define
 75   #   Jamf::Api::BaseClasses::Classic::Group
 76   # rather than what we want:
 77   #  Jamf::Group
 78   ###################################################
 79 
 80   loader.collapse("#{__dir__}/api")
 81 
 82   loader.collapse("#{__dir__}/api/classic")
 83   loader.collapse("#{__dir__}/api/classic/api_objects")
 84   loader.collapse("#{__dir__}/api/classic/base_classes")
 85 
 86   loader.collapse("#{__dir__}/api/jamf_pro")
 87   loader.collapse("#{__dir__}/api/jamf_pro/api_objects")
 88   loader.collapse("#{__dir__}/api/jamf_pro/mixins")
 89   loader.collapse("#{__dir__}/api/jamf_pro/base_classes")
 90   loader.collapse("#{__dir__}/api/jamf_pro/other_classes")
 91 
 92   ##### Inflected Paths
 93 
 94   # filenames => Constants, which don't adhere to zeitwerk's parsing standards.
 95   #
 96   # Mostly because the a filename like 'oapi_object' would be
 97   # loaded by zeitwerk expecting it to define 'OapiObject', but it really
 98   # defines 'OAPIObject'
 99   ###############################################
100 
101   # Connections
102   loader.inflector.inflect 'classic_api' => 'ClassicAPI'
103   loader.inflector.inflect 'jamf_pro_api' => 'JamfProAPI'
104   loader.inflector.inflect 'jamf_pro_api_error' => 'JamfProAPIError'
105 
106   # API objects, resources, and mixins
107   loader.inflector.inflect 'oapi_schemas' => 'OAPISchemas'
108   loader.inflector.inflect 'oapi_object' => 'OAPIObject'
109   loader.inflector.inflect 'oapi_validate' => 'OAPIValidate'
110 
111   loader.inflector.inflect 'jpapi_resource' => 'JPAPIResource'
112 
113   loader.inflector.inflect 'api_object' => 'APIObject'
114   loader.inflector.inflect 'api_role' => 'APIRole'
115   loader.inflector.inflect 'api_client' => 'APIClient'
116   loader.inflector.inflect 'api_integration' => 'APIIntegration'
117   loader.inflector.inflect 'xml_workaround' => 'XMLWorkaround'
118   loader.inflector.inflect 'json_object' => 'JSONObject'
119   loader.inflector.inflect 'vppable' => 'VPPable'
120   loader.inflector.inflect 'osx_configuration_profile' => 'OSXConfigurationProfile'
121   loader.inflector.inflect 'jp_extendable' => 'JPExtendable'
122   loader.inflector.inflect 'mdm' => 'MDM'
123   loader.inflector.inflect 'ibeacon' => 'IBeacon'
124   loader.inflector.inflect 'powerbroker_identity_services' => 'PowerBroker'
125   loader.inflector.inflect 'admitmac' => 'ADmitMac'
126   loader.inflector.inflect 'ip_address' => 'IPAddress'
127   loader.inflector.inflect 'netboot_server' => 'NetBootServer'
128   loader.inflector.inflect 'vpp_account' => 'VPPAccount'
129   loader.inflector.inflect 'removable_macaddr' => 'RemovableMacAddress'
130   loader.inflector.inflect 'md_prestage_name' => 'MobileDevicePrestageName'
131   loader.inflector.inflect 'md_prestage_names' => 'MobileDevicePrestageNames'
132   loader.inflector.inflect 'md_prestage_skip_setup_items' => 'MobileDevicePrestageSkipSetupItems'
133   loader.inflector.inflect 'macos_managed_updates' => 'MacOSManagedUpdates'
134   loader.inflector.inflect 'macos_redeploy_mgmt_framework' => 'MacOSRedeployMgmtFramework'
135   loader.inflector.inflect 'filevault' => 'FileVault'
136 
137   # deprecations, separated so they load only when used.
138   # When its time to get rid of them, delete the files from the
139   # 'deprecations' directory, and the matching line here.
140   loader.inflector.inflect('deprecated_api_constant' => 'API')
141   loader.inflector.inflect('deprecated_config_constant' => 'CONFIG')
142   loader.inflector.inflect('deprecated_api_connection_class' => 'APIConnection')
143 
144   ##### Ingored Paths
145 
146   # These should be ignored, some will be required directly
147   #####################################
148 
149   loader.ignore "#{__dir__}/db_connection.rb"
150   loader.ignore "#{__dir__}/ruby_extensions.rb"
151   loader.ignore "#{__dir__}/ruby_extensions"
152   loader.ignore "#{__dir__}/exceptions.rb"
153   loader.ignore "#{__dir__}/deprecations"
154   loader.ignore "#{__dir__}/deprecations.rb"
155 
156   lib_dir = Pathname.new(__dir__).parent.to_s
157   loader.ignore "#{lib_dir}/ruby-jss.rb"
158   loader.ignore "#{lib_dir}/jss.rb"
159   loader.ignore "#{lib_dir}/jss-api.rb"
160 
161   # See the comments and manual 'require' at the top of
162   # lib/jamf/api/jamf_pro/api_objects/api_role.rb
163   # for why we ignore this file.
164   loader.ignore "#{lib_dir}/jamf/api/jamf_pro/oapi_schemas/api_role.rb"
165 
166   ##### Callbacks
167 
168   # callback for when a specific file/constant loads
169   # duplicate and uncomment this if desired to react to
170   # specific things loading
171   #####################################
172   # loader.on_load('Jamf::SomeClass') do |klass, abspath|
173   #   Jamf.load_msg "I just loaded #{klass} from #{abspath}"
174   # end
175 
176   # callback for when anything loads
177   #  - const_path is like "Jamf::SomeClass" or "Jamf::SomeClass::SOME_CONST_ARRY"
178   #  - value is the value that constant contains after loading,
179   #    e.g. the class Jamf::SomeClass for 'Jamf::SomeClass' or
180   #    an Array for the constant  "Jamf::SomeClass::SOME_CONST_ARRY"
181   #  - abspath is the full path to the file where the constant was loaded from.
182   #####################################
183   loader.on_load do |const_path, value, abspath|
184     load_msg "Zeitwerk just loaded #{value.class} '#{const_path}' from:\n  #{abspath}"
185 
186     # Parse OAPI_PROPERTIES into getters and setters for subclasses of
187     # OAPIObject in the JPAPI.
188     #
189     # The class we just loaded must have this method and constant
190     # and the method must not have run already for the class or any superclass.
191     # This prevents running parse_oapi_properties again in subclasses that
192     # don't need to do that
193     if value.respond_to?(:oapi_properties_parsed?) && \
194        defined?(value::OAPI_PROPERTIES) && \
195        !value.oapi_properties_parsed?
196 
197       parsed = value.parse_oapi_properties
198       load_msg "Parsed OAPI_PROPERTIES for #{value}" if parsed
199     end
200 
201     # Generate the identifier list methods (.all_*) for subclasses of APIObject
202     # in the Classic API
203     if value.is_a?(Class) && value.superclass == Jamf::APIObject
204 
205       done = value.define_identifier_list_methods
206       load_msg "Defined identifier list methods for #{value}" if done
207     end
208   end
209 
210   loader.setup
211 end
verbose_loading?() click to toggle source

Only look at the filesystem once.

   # File lib/jamf/zeitwerk_config.rb
43 def self.verbose_loading?
44   return @verbose_loading unless @verbose_loading.nil?
45 
46   @verbose_loading = VERBOSE_LOADING_FILE.file?
47   @verbose_loading ||= ENV.include? VERBOSE_LOADING_ENV
48   @verbose_loading
49 end