#require "providers/provider_factory" require "exceptions/invalid_record" require "json" require 'db/validators/all' module Devops module Model class MongoModel attr_accessor :created_at # multi_json sends argument to 'to_json' method def to_json arg=nil JSON.pretty_generate self.to_hash end def to_hash h = to_hash_without_id h["id"] = self.id h end def to_mongo_hash h = to_hash_without_id h["_id"] = self.id h end def is_empty? val val.nil? or val.strip.empty? end def check_string! val raise ArgumentError unless val.is_a?(String) val.strip! raise ArgumentError if val.empty? end def check_array! val, type, empty=false raise ArgumentError unless val.is_a?(Array) raise ArgumentError if !empty and val.empty? val.each do |v| raise ArgumentError unless v.is_a?(type) end end def check_name_value val raise ArgumentError.new "Invalid name, it should contains 0-9, a-z, A-Z, _, - symbols only" if val.match(/^[0-9a-zA-Z_\-]+$/).nil? end def check_provider provider=self.provider unless ::Provider::ProviderFactory.providers.include?(provider) raise InvalidRecord.new "Invalid provider '#{provider}'" end end def validate! begin # TODO: we should validate type in request parser # self.validate_fields_types self.class.validate_model(self) true rescue InvalidRecord => e error_message = self.build_error_message(e.message) raise InvalidRecord.new(error_message) end end def validate_fields! result = [] self.class.field_validators.each do |field, validation_method| begin self.send(validation_method) rescue InvalidRecord => e result << {key: field, message: e.message} end end unless result.empty? raise InvalidRecord.new(error_data: result) end true end def build_error_message(message) # overrided in descendants message end # types - Hash # key - param name # value - Hash # :type - param type # :empty - can param be empty? (false) # :nil - can param be nil? (false) # :value_type - type of array element (String) # TODO: @deprecated def self.types types define_method :validate_fields_types do t = types.keys e = types.keys n = types.keys types.each do |name, value| if value[:nil] n.delete(name) if self.send(name).nil? e.delete(name) t.delete(name) next end else n.delete(name) unless self.send(name).nil? end if self.send(name).is_a? value[:type] t.delete(name) self.send(name).strip! if value[:type] == String if value[:type] == Array unless value[:value_type] == false type = value[:value_type] || String self.send(name).each do |e| unless e.is_a?(type) t.push(name) break end end end end e.delete(name) if value[:empty] or !self.send(name).empty? end end raise InvalidRecord.new "Parameter(s) '#{n.join("', '")}' can not be undefined" unless n.empty? raise InvalidRecord.new "Parameter(s) '#{t.join("', '")}' have invalid type(s)" unless t.empty? raise InvalidRecord.new "Parameter(s) '#{e.join("', '")}' can not be empty" unless e.empty? if types.has_key? :provider self.send("check_provider") end true end end @validators = [] @field_validators = {} class << self attr_accessor :validators attr_accessor :field_validators def inherited(subclass) subclass.validators = [] subclass.validators += self.validators subclass.field_validators = self.field_validators.clone end # all exceptions are handled in @validate! method def validate_model(model) validators.each do |validator| validator.new(model).validate! end end # validate field value # if method validate! returns false, then stop validation without error def set_field_validators field, *validators method_name = "validate_" + field.to_s + "!" define_method(method_name) do validators.each do |validator| break unless validator.new(self, send(field)).validate! end end self.field_validators[field] = method_name end # private class methods private def set_validators(*validators_to_add) self.validators += validators_to_add end def unset_validators(*validators_to_remove) self.validators -= validators_to_remove end end end end end