require "gql_serializer/version" require "gql_serializer/extensions" require "gql_serializer/configuration" module GqlSerializer def self.configuration @configuration ||= Configuration.new end def self.configure yield configuration end def self.parse_query(input) query = input.dup query.strip! query.gsub!(/[\r\n\t ]+/, ' ') query.gsub!(/\{ /, '{') query.gsub!(/ }/, '}') result, _ = self.parse_it(query) result end def self.query_include(model, hasharray) include_array = [] relations = model.reflections hasharray.each do |element| if element.is_a? String key = element.split(':')[0] include_array.push(key) if relations[key] elsif element.is_a? Hash key = element.keys.first.split(':')[0] relation_model = model.reflections[key].klass relation_hasharray = self.query_include(relation_model, element.values.first) if relation_hasharray.empty? include_array.push(key) else include_array.push({key => relation_hasharray}) end end end include_array end # example hasharray = ["id", "name", "tags", { "panels" => ["id", { "cards" => ["content"] }] }] def self.serialize(records, hasharray, options, instructions = {}) if records.nil? return nil end if records.respond_to? :map return records.map do |record| self.serialize(record, hasharray, options, instructions) end end record = records id = "#{record.class}, #{hasharray}" instruction = instructions[id] if (!instruction) instruction = {klass: record.class, hasharray: hasharray, relations: [], attributes: []} instructions[id] = instruction model = record.class all_relations = model.reflections.keys relations = hasharray.filter do |relation| next true if !relation.is_a?(String) key, alias_key = relation.split(':') all_relations.include?(key) end attributes = hasharray - relations attributes = model.attribute_names if attributes.empty? attributes.each do |attribute| key, alias_key = attribute.split(':') alias_key = apply_case(alias_key || key, options[:case]) instruction[:attributes].push({key: key, alias_key: alias_key}) end relations.each do |relation| if relation.is_a? String key, alias_key = relation.split(':') alias_key = apply_case(alias_key || key, options[:case]) instruction[:relations].push({key: key, alias_key: alias_key, hasharray: []}) else key, alias_key = relation.keys.first.split(':') alias_key = apply_case(alias_key || key, options[:case]) instruction[:relations].push({key: key, alias_key: alias_key, hasharray: relation.values.first}) end end end hash = {} instruction[:attributes].each do |attribute| hash[attribute[:alias_key]] = coerce_value(record.public_send(attribute[:key])) end instruction[:relations].each do |relation| relation_records = record.public_send(relation[:key]) hash[relation[:alias_key]] = self.serialize(relation_records, relation[:hasharray], options, instructions) end hash end def self.coerce_value(value) return value.to_f if value.is_a? BigDecimal return value.new_offset(0).strftime("%FT%TZ") if value.is_a? DateTime return value.utc.iso8601 if value.is_a? Time value end private def self.apply_case(key, key_case) case key_case when Configuration::CAMEL_CASE result = key.camelize result[0] = result[0].downcase when Configuration::SNAKE_CASE result = key.underscore else result = key end result end def self.parse_it(query) result = [] while query&.length&.> 0 if query[0] == ' ' query.strip! next elsif query[0] == '}' return result, query[1..-1] end next_key = query[/[_a-zA-Z0-9:]+/] query = query[next_key.length..-1] query.strip! if query.nil? || query.empty? || query[0].match?(/[_a-zA-Z0-9:]/) result.push(next_key) elsif query&.[](0) == '{' query = query[1..-1] obj, query = parse_it(query) result.push(next_key => obj) elsif query[0] == '}' result.push(next_key) return result, query[1..-1] else raise "unsupported character '#{query[0]}'" end end return result, nil end end