lib/svelte_on_rails/lib/to_svelte.rb



module SvelteOnRails
  module Lib
    class SvelteAttributes

      def initialize
        @labels = {}
      end

      def calculate_instance(record, attributes, call_stack: 0, offset: nil, limit: nil)

        next_stack = call_stack + 1

        if record.respond_to?(:each)

          recs2 = if offset || limit
                    if (record.is_a?(ActiveRecord::Relation) || record.is_a?(ActiveRecord::Associations::CollectionProxy) rescue false)
                      _recs = (offset ? record.offset(offset) : record)
                      (limit ? _recs.limit(limit) : _recs)
                    elsif record.respond_to?(:drop) && record.respond_to?(:take) # that might be a array
                      _recs = offset ? record.drop(offset) : record
                      limit ? _recs.take(limit) : _recs
                    else
                      raise "[svelte-on-rails:to_svelte] unknown class for records: #{record}"
                    end
                  else
                    record
                  end

          set_labels(record.first, attributes)

          values = recs2.map do |rec|
            calculate_instance(rec, attributes, call_stack: next_stack)
          end

        else

          # we have a single record

          values = {}

          set_labels(record, attributes)

          attributes.each do |attr|

            if attr.is_a? Hash

              # we have associations
              attr.each do |key, value|
                next if ['offset', 'limit'].include?(key.to_s)
                _offset, _limit, _value = extract_limit(value)

                _key = key.to_s

                # inspect association and set_labels

                reflect = record.class.reflect_on_association(_key)
                raise "invalid association: #{_key}" unless reflect
                set_labels(reflect, value)

                # values

                recs = record.send(_key)
                if recs.present?
                  if recs.respond_to?(:each)
                    values[_key] = calculate_instance(
                      recs,
                      value,
                      call_stack: next_stack,
                      offset: _offset,
                      limit: _limit
                    )
                  else
                    values[_key] = calculate_instance(
                      recs,
                      value,
                      call_stack: next_stack
                    )
                  end
                end
              end

            else
              # we have attributes
              raise 'Invalid attribute' unless [Symbol, String].include?(attr.class)
              _key = attr.to_s

              values[_key] = record.send(_key)
            end

          end
        end

        if call_stack >= 1
          values
        else
          { 'values' => values }.merge(@labels)
        end

      end

      def calculate_class(model, attributes, call_stack: 0)

        next_stack = call_stack + 1

        set_labels(model, attributes)

        hash = attributes.find { |element| element.is_a?(Hash) } || {}
        hash.each do |key, value|
          reflect = model.reflect_on_association(key.to_s)
          if reflect
            calculate_class(
              reflect,
              value,
              call_stack: next_stack
            )
          end
        end

        if call_stack == 0
          @labels
        end
      end

      def calculate_relation(relation, attributes, call_stack: 0)
        set_labels(relation.klass, attributes)
        values = relation.map do |rec|
          calculate_instance(rec, attributes)['values']
        end
        {
          'values' => values
        }.merge(@labels)
      end

      private

      def extract_limit(attributes)

        _hash_args = attributes.grep(Hash).first.dup
        attr, lim = if _hash_args.present?
                      hash_args = _hash_args.transform_keys { |key| key.to_s } # multiple arrays is not possible
                      hash_remainder = hash_args.reject { |key, _| %w[offset limit].include?(key.to_s) }
                      _attr = attributes.reject { |item| item.is_a?(Hash) }
                      [
                        if hash_remainder.any?
                          _attr + [hash_remainder]
                        else
                          _attr
                        end,
                        hash_args
                      ]
                    else
                      [
                        attributes,
                        {}
                      ]
                    end

        [
          lim['offset'],
          lim['limit'],
          attr
        ]
      end

      def set_labels(record, keys)

        first_hash = keys.find { |element| element.is_a?(Hash) }
        _keys = keys.reject { |element| element.is_a?(Hash) }
        _keys += first_hash.keys if first_hash

        _keys.each do |attr|
          unless attr.respond_to?(:each)
            obj = if record.respond_to?(:human_attribute_name)
                    record
                  elsif record.class.respond_to?(:human_attribute_name)
                    record.class
                  end

            next unless obj

            @labels["#{obj.to_s.underscore}_labels"] ||= {}
            @labels["#{obj.to_s.underscore}_labels"][attr.to_s] ||= obj.human_attribute_name(attr)

          end
        end
      end

    end
  end
end