lib/svelte_on_rails/lib/to_svelte.rb



module SvelteOnRails
  module Lib
    class SvelteAttributes

      def initialize
        @labels = {}
      end

      def calculate_instance(record, attributes, associations, 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, associations, call_stack: next_stack)
          end

        else

          # we have a single record

          values = {}

          set_labels(record, attributes, associations)

          attributes.each do |attr|
            raise 'Invalid attribute' unless [Symbol, String].include?(attr.class)
            raise "[svelte-on-rails:to_svelte] #{record.class} does not respond to: #{attr}" unless record.respond_to?(attr)
            _key = attr.to_s

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

          associations.each do |key, val|

            # we have associations
            val.each do |_key|
              next if ['offset', 'limit'].include?(_key.to_s)
              raise "[svelte-on-rails:to_svelte] #{record.class} does not respond to: #{key}" unless record.respond_to?(key)
              _offset, _limit, _value = extract_limit(val)

              _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, val)

              # values

              recs = record.send(_key)
              if recs.present?
                if recs.respond_to?(:each)
                  values[_key] = calculate_instance(
                    recs,
                    val.reject{|v|v.is_a?(Hash)},
                    {},
                    call_stack: next_stack,
                    offset: _offset,
                    limit: _limit
                  )
                else
                  values[_key] = calculate_instance(
                    recs,
                    val,
                    {},
                    call_stack: next_stack
                  )
                end
              end
            end
          end
        end

        if call_stack >= 1
          values
        else
          [
            @labels,
            values
          ]
        end

      end

      def calculate_class(model, attributes, associations, call_stack: 0)

        next_stack = call_stack + 1

        set_labels(model, attributes, associations)

        associations.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, associations)
        set_labels(relation.klass, attributes)
        r = relation.map do |rec|
          calculate_instance(rec, attributes, associations, call_stack: 1)
        end

        @labels.merge({
                        relation.klass.to_s.underscore.pluralize => r
                      })

      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, assoc = {})

        _keys = keys.reject { |element| element.is_a?(Hash) } + assoc.keys

        _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