File: options.rb

package info (click to toggle)
ruby-grape-entity 1.0.1-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 456 kB
  • sloc: ruby: 3,335; makefile: 6
file content (132 lines) | stat: -rw-r--r-- 3,309 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true

require 'forwardable'

module Grape
  class Entity
    class Options
      extend Forwardable

      attr_reader :opts_hash

      def_delegators :opts_hash, :dig, :key?, :fetch, :[], :empty?

      def initialize(opts_hash = {})
        @opts_hash = opts_hash
        @has_only = !opts_hash[:only].nil?
        @has_except = !opts_hash[:except].nil?
        @for_nesting_cache = {}
        @should_return_key_cache = {}
      end

      def merge(new_opts)
        return self if new_opts.empty?

        merged = if new_opts.instance_of? Options
                   @opts_hash.merge(new_opts.opts_hash)
                 else
                   @opts_hash.merge(new_opts)
                 end

        Options.new(merged)
      end

      def reverse_merge(new_opts)
        return self if new_opts.empty?

        merged = if new_opts.instance_of? Options
                   new_opts.opts_hash.merge(@opts_hash)
                 else
                   new_opts.merge(@opts_hash)
                 end

        Options.new(merged)
      end

      def ==(other)
        other_hash = other.is_a?(Options) ? other.opts_hash : other
        @opts_hash == other_hash
      end

      def should_return_key?(key)
        return true unless @has_only || @has_except

        only = only_fields.nil? ||
               only_fields.key?(key)
        except = except_fields&.key?(key) &&
                 except_fields[key] == true
        only && !except
      end

      def for_nesting(key)
        @for_nesting_cache[key] ||= build_for_nesting(key)
      end

      def only_fields(for_key = nil)
        return nil unless @has_only

        @only_fields ||= @opts_hash[:only].each_with_object({}) do |attribute, allowed_fields|
          build_symbolized_hash(attribute, allowed_fields)
        end

        only_for_given(for_key, @only_fields)
      end

      def except_fields(for_key = nil)
        return nil unless @has_except

        @except_fields ||= @opts_hash[:except].each_with_object({}) do |attribute, allowed_fields|
          build_symbolized_hash(attribute, allowed_fields)
        end

        only_for_given(for_key, @except_fields)
      end

      def with_attr_path(part)
        return yield unless part

        stack = (opts_hash[:attr_path] ||= [])
        stack.push part
        result = yield
        stack.pop
        result
      end

      private

      def build_for_nesting(key)
        Options.new(
          opts_hash.dup.reject { |current_key| current_key == :collection }.merge(
            root: nil,
            only: only_fields(key),
            except: except_fields(key),
            attr_path: opts_hash[:attr_path]
          )
        )
      end

      def build_symbolized_hash(attribute, hash)
        case attribute
        when Hash
          attribute.each do |attr, nested_attrs|
            hash[attr.to_sym] = build_symbolized_hash(nested_attrs, {})
          end
        when Array
          return attribute.each { |x| build_symbolized_hash(x, {}) }
        else
          hash[attribute.to_sym] = true
        end

        hash
      end

      def only_for_given(key, fields)
        if key && fields[key].is_a?(Array)
          fields[key]
        elsif key.nil?
          fields
        end
      end
    end
  end
end