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) return SupplierEmployeeSettings::NullObject.new unless employee.present? if employee_ids.include? employee.id settings = SupplierEmployeeSettings.new(self, employee, dictionary[employee.id]) else settings = SupplierEmployeeSettings::NullObject.new end employee.settings = settings 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 to_store! 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.to_store! end end class SupplierEmployeeSettings delegate :as_json, :to_json, to: :settings attr_reader :employee, :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 def public_send(m, *args) if ::Employee::DEFAULT_SETTINGS.has_key?(m.to_s) ::Employee::DEFAULT_SETTINGS[m.to_s] else nil # this time we will raise on further invocation, since then there is something wrong end end def as_json {} end end def initialize(all_employees_settings, employee, settings = {}) @all_employees_settings = all_employees_settings @employee = employee @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[employee.id][attribute] = value persist! if persist self end def []=(attribute, value) set attribute, value end def set!(attribute, value) set attribute, value, persist: true end def update(params) Employee::DEFAULT_SETTINGS.keys.each do |attribute| if params.has_key?(attribute) new_value = params.delete(attribute) set attribute, new_value end end params end def update!(params) update params persist! params end def persist! all_employees_settings.supplier.is_dirty all_employees_settings.supplier.save end def supplier all_employees_settings.supplier 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 set_value = true attribute.chop! end if method.end_with? '?' # Always a getter, so no set_value attribute.chop! #attribute = attribute.from(7) if attribute.start_with? 'is_not' 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