I'm in the process of writing a Rails engine that requires some setup config and also does a check to see if some required attributes are defined on a model. The engine class in lib that defines this is as follows.
module Kiosk
mattr_accessor :kiosk_class
@@kiosk_class = 'Computer'
mattr_accessor :kiosk_primary_key
@@kiosk_primary_key = 'id'
mattr_accessor :kiosk_type_foreign_key
@@kiosk_type_foreign_key = 'kiosk_type_id'
mattr_accessor :kiosk_name_attribute
@@kiosk_name_attribute = 'name'
mattr_accessor :kiosk_ip_address_attribute
@@kiosk_ip_address_attribute = 'ip_address'
mattr_accessor :kiosk_mac_address_attribute
@@kiosk_mac_address_attribute = 'mac_address'
# Private config and methods
def self.setup
if block_given?
yield self
end
check_fields!
end
def self.required_fields
[@@kiosk_primary_key, @@kiosk_name_attribute, @@kiosk_ip_address_attribute, @@kiosk_mac_address_attribute, @@kiosk_type_foreign_key]
end
def self.check_fields!
failed_attributes = []
instance = @@kiosk_class.constantize.new
required_fields.each do |field|
failed_attributes << field unless instance.respond_to?(field)
end
if failed_attributes.any?
raise "Missing required attributes in #{@@kiosk_class} model: #{failed_attributes.join(', ')}"
end
end
def self.klass
@@kiosk_class.constantize
end
end
The model name and attribute names can be configured in a required initializer that does the job of calling setup
passing a block of config parameters. That is,
Kiosk.setup do |config|
# The name of the class that stores info about the kiosks
# It should contain the required fields whose names are defined below
# config.kiosk_class = 'Computer'
# The primary key of the kiosk class
# config.kiosk_primary_key = 'id'
# A foreign key in the kiosk class for the kiosk type
# config.kiosk_type_foreign_key = 'kiosk_type_id'
# An attribute containing the name of the kiosk
# config.kiosk_name_attribute = 'name'
# An attribute containing the IP address of the kiosk
# config.kiosk_ip_address_attribute = 'ip_address'
# An attribute containing the MAC address of the kiosk
# config.kiosk_mac_address_attribute = 'mac_address'
end
The problem that I encountered during testing is that if a required attribute is missing then calling any generator or Rake task also fails meaning the attribute cannot even be added.
What I would like is to be able to detect in my setup whether it is being called as part of the server start (and therefore should do the fields check) or as any other Rails startup such as Rake tasks, generators, etc. (and therefore should skip the fields check). I feel there must be a solution because starting the Rails console never fails.
Alternatively, if this is not possible, how would you perform the fields check outside of the initializer but in a way that is guaranteed to occur during server startup and only have it happen once per startup?
I realise that similar questions have been asked before on knowing what context an app is running under (e.g. How to prevent initializers from running when running `rails generate` and Rails 3 initializers that run only on `rails server` and not `rails generate`, etc) but the solutions presented there are not very useful for my situation.