class SupplierEmployeesSettings attr_reader :dictionary, :employee_ids, :supplier def initialize(supplier) @supplier = supplier @dictionary = (supplier.employee_settings_storage || {}).to_h @employee_ids = supplier.employee_ids || [] end def for_employee(employee_id) employee_id = employee_id.id if employee_id.is_a?(Employee) if employee_ids.include? employee_id SupplierEmployeeSettings.new(self, employee_id, dictionary[employee_id]) else SupplierEmployeeSettings::NullObject.new end end def [](val) val = val.to_s if dictionary[val].is_a? SettingsPersistor dictionary[val] else dictionary[val] = SettingsPersistor.new(self, dictionary[val]) end end def persist supplier.employee_settings_storage = dictionary end def method_missing(m, *args) dictionary.send(m, *args) end class SettingsPersistor < Hash attr_reader :all_employees_settings def initialize(all_employees_settings, options = {}) @all_employees_settings = all_employees_settings self.replace options if options.is_a? Hash end alias_method :orig_setter, :[]= def []=(*args) orig_setter(*args) all_employees_settings.persist end end class SupplierEmployeeSettings delegate :as_json, :to_json, to: :settings attr_reader :id, :settings, :all_employees_settings class NullObject < ::NullObject ::Employee::DEFAULT_SETTINGS.each do |attribute, value| if value == true or value == false define_method("#{attribute}?"){ value } end define_method(attribute){ value } end end def initialize(all_employees_settings, employee_id, settings = {}) @all_employees_settings = all_employees_settings @id = employee_id @settings = Employee.default_settings.merge(settings || {}) end def [](val) settings[val.to_s] end def set(attribute, value, persist: false) settings[attribute] = value all_employees_settings[id][attribute] = value if employee = all_employees_settings.supplier.employees.find{|e| e.id == id} employee.public_send("#{attribute}=", value) end all_employees_settings.supplier.save if persist self end def []=(attribute, value) set attribute, value end def set!(attribute, value) set attribute, value, persist: true end # Parse a method name to its underlying operations # settings.is_manager? # is a getter for the attribute manager # settings.is_manager! # is a boolean setter to true for the attribute manager and persists the inderlying model # without exclamation mark it is the same, but without persisting the model. # settings.is_not_manager! # is a boolean setter to false for the attribute manger. # settings.manager # is a normal accessor for the attribute manager def parse_method_for_boolean_operation(method) method = method.to_s persist = false attribute = method set_value = nil if method.end_with? '!' persist = true attribute.chop! end if method.end_with? '?' # Always a getter, so no set_value attribute.chop! attribute = attribute.from(3) if attribute.start_with? 'is_' elsif method.start_with? 'is_not_' set_value = false attribute = attribute.from 7 elsif method.start_with? 'is_' set_value = true attribute = attribute.from 3 end raise "Settings do not support attribute '#{attribute}'" unless Employee.default_settings.has_key?(attribute) # Do not return a nonexistent attribute [attribute, set_value, persist] end def method_missing(m, *args) attribute, set_value, persist = parse_method_for_boolean_operation(m) return unless attribute # method cannot be used in the settings return settings[attribute] if set_value.nil? # Getter operation, setting nil is not supported set attribute, set_value, persist: persist end end end