File: wrapper.rb

package info (click to toggle)
ruby-gh 0.21.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,644 kB
  • sloc: ruby: 1,793; makefile: 4
file content (235 lines) | stat: -rw-r--r-- 5,414 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# frozen_string_literal: true

require 'gh'
require 'addressable/uri'

module GH
  # Public: Simple base class for low level layers.
  # Handy if you want to manipulate resources coming in from Github.
  #
  # Examples
  #
  #   class IndifferentAccess
  #     def [](key) super.tap { |r| r.data.with_indifferent_access! } end
  #   end
  #
  #   gh = IndifferentAccess.new
  #   gh['users/rkh'][:name] # => "Konstantin Haase"
  #
  #   # easy to use in the low level stack
  #   gh = Github.build do
  #     use GH::Cache
  #     use IndifferentAccess
  #     use GH::Normalizer
  #   end
  class Wrapper
    extend Forwardable
    include Case

    # Public: Get wrapped layer.
    attr_reader :backend

    # Public: ...
    attr_reader :options

    # Public: Returns the URI used for sending out web request.
    def_delegator :backend, :api_host

    # Internal: ...
    def_delegator :backend, :http

    # Internal: ...
    def_delegator :backend, :request

    # Public: ...
    def_delegator :backend, :post

    # Public: ...
    def_delegator :backend, :delete

    # Public: ...
    def_delegator :backend, :head

    # Public: ...
    def_delegator :backend, :patch

    # Public: ...
    def_delegator :backend, :put

    # Public: ...
    def_delegator :backend, :fetch_resource

    # Public: ...
    def_delegator :backend, :in_parallel

    # Public: ...
    def_delegator :backend, :in_parallel?

    # Public: ...
    def_delegator :backend, :full_url

    # Public: ...
    def_delegator :backend, :path_for

    def self.double_dispatch
      define_method(:modify) { |data| double_dispatch(data) }
    end

    # Public: Retrieves resources from Github.
    def self.[](key)
      new[key]
    end

    # Public: Retrieves resources from Github.
    #
    # By default, this method is delegated to the next layer on the stack
    # and modify is called.
    def [](key)
      generate_response key, fetch_resource(key)
    end

    # Internal: ...
    def generate_response(key, resource)
      modify backend.generate_response(key, resource)
    end

    # Internal: Get/set default layer to wrap when creating a new instance.
    def self.wraps(klass = nil)
      @wraps = klass if klass
      @wraps ||= Remote
    end

    # Public: Initialize a new Wrapper.
    #
    # backend - layer to be wrapped
    # options - config options
    def initialize(backend = nil, options = {})
      backend, @options = normalize_options(backend, options)
      @options.each_pair { |key, value| public_send("#{key}=", value) if respond_to? "#{key}=" }
      setup(backend, @options)
    end

    # Public: Set wrapped layer.
    def backend=(layer)
      reset if backend
      layer.frontend = self
      @backend = layer
    end

    # Internal: ...
    attr_writer :frontend

    # Internal: ...
    def frontend
      @frontend ? @frontend.frontend : self
    end

    # Public: ...
    def inspect
      "#<#{self.class}: #{backend.inspect}>"
    end

    # Internal: ...
    def prefixed(key)
      "#{prefix}##{identifier(key)}"
    end

    # Public: ...
    def reset
      backend&.reset
    end

    # Public: ...
    def load(data)
      modify backend.load(data)
    end

    private

    def identifier(key)
      backend.prefixed(key)
    end

    def prefix
      self.class.name
    end

    def double_dispatch(data)
      case data
      when respond_to(:to_gh) then modify_response(data)
      when respond_to(:to_hash) then modify_hash(data)
      when respond_to(:to_ary) then modify_array(data)
      when respond_to(:to_str) then modify_string(data)
      when respond_to(:to_int) then modify_integer(data)
      else modify_unknown data
      end
    rescue StandardError => e
      raise Error.new(e, data)
    end

    def modify_response(response)
      result = double_dispatch response.data
      result.respond_to?(:to_gh) ? result.to_gh : Response.new(result, response.headers, response.url)
    end

    def modify(data, *)
      data
    rescue StandardError => e
      raise Error.new(e, data)
    end

    def modify_array(array)
      array.map { |e| modify(e) }
    end

    def modify_hash(hash)
      corrected = {}
      hash.each_pair { |k, v| corrected[k] = modify(v) }
      corrected.default_proc = hash.default_proc if hash.default_proc
      corrected
    end

    alias modify_string modify
    alias modify_integer modify
    alias modify_unknown modify

    def setup(backend, options)
      self.backend = backend.is_a?(Wrapper) ? backend : self.class.wraps.new(backend, options)
    end

    def normalize_options(backend, options)
      if backend.is_a?(Hash)
        options = backend
        backend = nil
      end
      options ||= {}
      backend ||= options[:backend] || options[:api_url] || 'https://api.github.com'
      [backend, options]
    end

    def setup_default_proc(hash, &block)
      old_proc = hash.default_proc
      hash.default_proc = proc do |h, key|
        value = old_proc.call(h, key) if old_proc
        value = block[h, key] if value.nil?
        value
      end
    end

    def setup_lazy_loading(hash, *args)
      loaded = false
      setup_default_proc(hash) do |h, key|
        next if loaded

        fields = lazy_load(h, key, *args)
        if fields
          modify_hash(fields)
          h.merge!(fields)
          loaded = true
          fields[key]
        end
      end
      hash
    end
  end
end