graphiti 1.0.rc.2 → 1.0.rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/graphiti.gemspec +1 -0
- data/lib/graphiti.rb +4 -0
- data/lib/graphiti/adapters/graphiti_api.rb +89 -0
- data/lib/graphiti/debugger.rb +3 -1
- data/lib/graphiti/errors.rb +42 -0
- data/lib/graphiti/query.rb +37 -4
- data/lib/graphiti/resource.rb +4 -0
- data/lib/graphiti/resource/configuration.rb +7 -0
- data/lib/graphiti/resource/persistence.rb +1 -1
- data/lib/graphiti/resource/remote.rb +68 -0
- data/lib/graphiti/resource/sideloading.rb +1 -0
- data/lib/graphiti/resource_proxy.rb +11 -6
- data/lib/graphiti/schema.rb +14 -1
- data/lib/graphiti/scope.rb +11 -5
- data/lib/graphiti/sideload.rb +52 -2
- data/lib/graphiti/sideload/belongs_to.rb +11 -1
- data/lib/graphiti/sideload/has_many.rb +11 -1
- data/lib/graphiti/util/persistence.rb +3 -0
- data/lib/graphiti/util/remote_params.rb +115 -0
- data/lib/graphiti/util/remote_serializer.rb +53 -0
- data/lib/graphiti/version.rb +1 -1
- metadata +20 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 673ff2ee2d99cc9cb04d8cfb86b5b2b03839b227
         | 
| 4 | 
            +
              data.tar.gz: a07ef309b47401d52a75f9bdbd6675d09fd83117
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 2f8911140b4fe3eb64420469e5ea428a7f7b9dc613d5eb5cf0acfa2169b4e20191e5be00f9c526770b8a05aa7de37d6dc5a84551bd0c4784532d6e7dde4c2d33
         | 
| 7 | 
            +
              data.tar.gz: b920e16d339cbbd8c2b7f19fe539c02cf1ae8426b83fe9125854132474f46163075f945f4e29416b86febcf6458ac24828f3a956db7c8dc5cc9a8b39a7740362
         | 
    
        data/graphiti.gemspec
    CHANGED
    
    | @@ -25,6 +25,7 @@ Gem::Specification.new do |spec| | |
| 25 25 | 
             
              spec.add_dependency 'concurrent-ruby', '~> 1.0'
         | 
| 26 26 | 
             
              spec.add_dependency 'activesupport', ['>= 4.1', '< 6']
         | 
| 27 27 |  | 
| 28 | 
            +
              spec.add_development_dependency "faraday", '~> 0.15'
         | 
| 28 29 | 
             
              spec.add_development_dependency "activerecord", ['>= 4.1', '< 6']
         | 
| 29 30 | 
             
              spec.add_development_dependency "kaminari", '~> 0.17'
         | 
| 30 31 | 
             
              spec.add_development_dependency "bundler"
         | 
    
        data/lib/graphiti.rb
    CHANGED
    
    | @@ -29,6 +29,7 @@ require "graphiti/resource/interface" | |
| 29 29 | 
             
            require "graphiti/resource/polymorphism"
         | 
| 30 30 | 
             
            require "graphiti/resource/documentation"
         | 
| 31 31 | 
             
            require "graphiti/resource/persistence"
         | 
| 32 | 
            +
            require "graphiti/resource/remote"
         | 
| 32 33 | 
             
            require "graphiti/sideload"
         | 
| 33 34 | 
             
            require "graphiti/sideload/has_many"
         | 
| 34 35 | 
             
            require "graphiti/sideload/belongs_to"
         | 
| @@ -65,7 +66,10 @@ require "graphiti/util/serializer_attributes" | |
| 65 66 | 
             
            require "graphiti/util/serializer_relationships"
         | 
| 66 67 | 
             
            require "graphiti/util/class"
         | 
| 67 68 | 
             
            require "graphiti/util/link"
         | 
| 69 | 
            +
            require "graphiti/util/remote_serializer"
         | 
| 70 | 
            +
            require "graphiti/util/remote_params"
         | 
| 68 71 | 
             
            require 'graphiti/adapters/null'
         | 
| 72 | 
            +
            require 'graphiti/adapters/graphiti_api'
         | 
| 69 73 | 
             
            require "graphiti/extensions/extra_attribute"
         | 
| 70 74 | 
             
            require "graphiti/extensions/boolean_attribute"
         | 
| 71 75 | 
             
            require "graphiti/extensions/temp_id"
         | 
| @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            module Graphiti
         | 
| 2 | 
            +
              module Adapters
         | 
| 3 | 
            +
                class GraphitiAPI < ::Graphiti::Adapters::Null
         | 
| 4 | 
            +
                  def base_scope(model)
         | 
| 5 | 
            +
                    {}
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def resolve(scope)
         | 
| 9 | 
            +
                    url = build_url(scope)
         | 
| 10 | 
            +
                    response = resource.make_request(url)
         | 
| 11 | 
            +
                    json = JSON.parse(response.body)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    if json['errors']
         | 
| 14 | 
            +
                      handle_remote_error(url, json)
         | 
| 15 | 
            +
                    else
         | 
| 16 | 
            +
                      models = json['data'].map { |d| build_entity(json, d) }
         | 
| 17 | 
            +
                      Util::RemoteSerializer.for(resource.class.serializer, models)
         | 
| 18 | 
            +
                      models
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  private
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def handle_remote_error(url, json)
         | 
| 25 | 
            +
                    errors = json['errors'].map do |error|
         | 
| 26 | 
            +
                      if raw = error['meta'].try(:[], '__raw_error__')
         | 
| 27 | 
            +
                        { message: raw['message'], backtrace: raw['backtrace'] }
         | 
| 28 | 
            +
                      else
         | 
| 29 | 
            +
                        { message: "#{error['title']} - #{error['detail']}" }
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
                    end.compact
         | 
| 32 | 
            +
                    raise Errors::Remote.new(url, errors)
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def build_url(scope)
         | 
| 36 | 
            +
                    url = resource.remote_url
         | 
| 37 | 
            +
                    params = scope[:params].merge(scope.except(:params))
         | 
| 38 | 
            +
                    params = CGI.unescape(params.to_query)
         | 
| 39 | 
            +
                    url = "#{url}?#{params}" unless params.blank?
         | 
| 40 | 
            +
                    url
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def find_entity(json, id, type)
         | 
| 44 | 
            +
                    lookup = Array(json['data']) | Array(json['included'])
         | 
| 45 | 
            +
                    lookup.find { |l| l['id'] == id.to_s && l['type'] == type }
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def build_entity(json, node)
         | 
| 49 | 
            +
                    entity = OpenStruct.new(node['attributes'])
         | 
| 50 | 
            +
                    entity.id = node['id']
         | 
| 51 | 
            +
                    entity._type = node['type']
         | 
| 52 | 
            +
                    process_relationships(entity, json, node['relationships'] || {})
         | 
| 53 | 
            +
                    entity
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def process_relationships(entity, json, relationship_json)
         | 
| 57 | 
            +
                    entity._relationships = {}
         | 
| 58 | 
            +
                    relationship_json.each_pair do |name, hash|
         | 
| 59 | 
            +
                      if data = hash['data']
         | 
| 60 | 
            +
                        if data.is_a?(Array)
         | 
| 61 | 
            +
                          data.each do |d|
         | 
| 62 | 
            +
                            rel = find_entity(json, d['id'], d['type'])
         | 
| 63 | 
            +
                            related_entity = build_entity(json, rel)
         | 
| 64 | 
            +
                            add_relationship(entity, related_entity, name, true)
         | 
| 65 | 
            +
                          end
         | 
| 66 | 
            +
                        else
         | 
| 67 | 
            +
                          rel = find_entity(json, hash['data']['id'], hash['data']['type'])
         | 
| 68 | 
            +
                          related_entity = build_entity(json, rel)
         | 
| 69 | 
            +
                          add_relationship(entity, related_entity, name)
         | 
| 70 | 
            +
                        end
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                      Util::RemoteSerializer.for(Graphiti::Serializer, Array(entity[name]))
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  def add_relationship(entity, related_entity, name, many = false)
         | 
| 77 | 
            +
                    if many
         | 
| 78 | 
            +
                      entity[name] ||= []
         | 
| 79 | 
            +
                      entity[name] << related_entity
         | 
| 80 | 
            +
                      entity._relationships[name] ||= []
         | 
| 81 | 
            +
                      entity._relationships[name] << related_entity
         | 
| 82 | 
            +
                    else
         | 
| 83 | 
            +
                      entity[name] = related_entity
         | 
| 84 | 
            +
                      entity._relationships[name] = related_entity
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
            end
         | 
    
        data/lib/graphiti/debugger.rb
    CHANGED
    
    | @@ -17,7 +17,9 @@ module Graphiti | |
| 17 17 | 
             
                      on_data_exception(payload, params)
         | 
| 18 18 | 
             
                    else
         | 
| 19 19 | 
             
                      if payload[:sideload]
         | 
| 20 | 
            -
                         | 
| 20 | 
            +
                        if payload[:results]
         | 
| 21 | 
            +
                          on_sideload_data(payload, params, took)
         | 
| 22 | 
            +
                        end
         | 
| 21 23 | 
             
                      else
         | 
| 22 24 | 
             
                        on_primary_data(payload, params, took)
         | 
| 23 25 | 
             
                      end
         | 
    
        data/lib/graphiti/errors.rb
    CHANGED
    
    | @@ -16,6 +16,36 @@ The adapter #{@adapter.class} does not implement method '#{@method}', which was | |
| 16 16 | 
             
                  end
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| 19 | 
            +
                class SideloadConfig < Base
         | 
| 20 | 
            +
                  def initialize(name, parent_resource_class, message)
         | 
| 21 | 
            +
                    @name = name
         | 
| 22 | 
            +
                    @parent_resource_class = parent_resource_class
         | 
| 23 | 
            +
                    @message = message
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def message
         | 
| 27 | 
            +
                    <<-MSG
         | 
| 28 | 
            +
            #{@parent_resource_class} sideload :#{@name} - #{@message}
         | 
| 29 | 
            +
                    MSG
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                class Remote < Base
         | 
| 34 | 
            +
                  def initialize(url, errors)
         | 
| 35 | 
            +
                    @url = url
         | 
| 36 | 
            +
                    @errors = errors
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def message
         | 
| 40 | 
            +
                    msg = "Error hitting remote API: #{@url}"
         | 
| 41 | 
            +
                    @errors.each do |e|
         | 
| 42 | 
            +
                      msg << "\n\n#{e[:message]}"
         | 
| 43 | 
            +
                      msg << "\n\n#{e[:backtrace].join("\n")}\n\n\n\n" if e[:backtrace]
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                    msg
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 19 49 | 
             
                class AroundCallbackProc < Base
         | 
| 20 50 | 
             
                  def initialize(resource_class, method_name)
         | 
| 21 51 | 
             
                    @resource_class = resource_class
         | 
| @@ -29,6 +59,18 @@ The adapter #{@adapter.class} does not implement method '#{@method}', which was | |
| 29 59 | 
             
                  end
         | 
| 30 60 | 
             
                end
         | 
| 31 61 |  | 
| 62 | 
            +
                class RemoteWrite < Base
         | 
| 63 | 
            +
                  def initialize(resource_class)
         | 
| 64 | 
            +
                    @resource_class = resource_class
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def message
         | 
| 68 | 
            +
                    <<-MSG
         | 
| 69 | 
            +
            #{@resource_class}: Tried to perform write operation. Writes are not supported for remote resources - hit the endpoint directly.
         | 
| 70 | 
            +
                    MSG
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 32 74 | 
             
                class UnsupportedOperator < Base
         | 
| 33 75 | 
             
                  def initialize(resource, filter_name, supported, operator)
         | 
| 34 76 | 
             
                    @resource = resource
         | 
    
        data/lib/graphiti/query.rb
    CHANGED
    
    | @@ -64,15 +64,27 @@ module Graphiti | |
| 64 64 | 
             
                  end
         | 
| 65 65 | 
             
                end
         | 
| 66 66 |  | 
| 67 | 
            +
                def resource_for_sideload(sideload)
         | 
| 68 | 
            +
                  if @resource.remote?
         | 
| 69 | 
            +
                    Class.new(Graphiti::Resource) do
         | 
| 70 | 
            +
                      self.remote = '_remote_sideload_'
         | 
| 71 | 
            +
                    end.new
         | 
| 72 | 
            +
                  else
         | 
| 73 | 
            +
                    sideload.resource
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 67 77 | 
             
                def sideloads
         | 
| 68 78 | 
             
                  @sideloads ||= begin
         | 
| 69 79 | 
             
                    {}.tap do |hash|
         | 
| 70 80 | 
             
                      include_hash.each_pair do |key, sub_hash|
         | 
| 71 81 | 
             
                        sideload = @resource.class.sideload(key)
         | 
| 72 | 
            -
             | 
| 82 | 
            +
             | 
| 83 | 
            +
                        if sideload || @resource.remote?
         | 
| 84 | 
            +
                          sl_resource = resource_for_sideload(sideload)
         | 
| 73 85 | 
             
                          _parents = parents + [self]
         | 
| 74 86 | 
             
                          sub_hash = sub_hash[:include] if sub_hash.has_key?(:include)
         | 
| 75 | 
            -
                          hash[key] = Query.new( | 
| 87 | 
            +
                          hash[key] = Query.new(sl_resource, @params, key, sub_hash, _parents)
         | 
| 76 88 | 
             
                        else
         | 
| 77 89 | 
             
                          handle_missing_sideload(key)
         | 
| 78 90 | 
             
                        end
         | 
| @@ -131,7 +143,9 @@ module Graphiti | |
| 131 143 | 
             
                    [].tap do |arr|
         | 
| 132 144 | 
             
                      sort_hashes do |key, value, type|
         | 
| 133 145 | 
             
                        if legacy_nested?(type)
         | 
| 134 | 
            -
                          @resource. | 
| 146 | 
            +
                          unless @resource.remote?
         | 
| 147 | 
            +
                            @resource.get_attr!(key, :sortable, request: true)
         | 
| 148 | 
            +
                          end
         | 
| 135 149 | 
             
                          arr << { key => value }
         | 
| 136 150 | 
             
                        elsif !type && top_level? && validate!(key, :sortable)
         | 
| 137 151 | 
             
                          arr << { key => value }
         | 
| @@ -196,6 +210,24 @@ module Graphiti | |
| 196 210 | 
             
                  not [false, 'false'].include?(@params[:paginate])
         | 
| 197 211 | 
             
                end
         | 
| 198 212 |  | 
| 213 | 
            +
                # If this is a remote call, we don't care about local parents
         | 
| 214 | 
            +
                def chain
         | 
| 215 | 
            +
                  if @resource.remote
         | 
| 216 | 
            +
                    top_remote_parent = parents.find { |p| p.resource.remote? }
         | 
| 217 | 
            +
                    [].tap do |chain|
         | 
| 218 | 
            +
                      parents.each do |p|
         | 
| 219 | 
            +
                        chain << p.association_name unless p == top_remote_parent
         | 
| 220 | 
            +
                      end
         | 
| 221 | 
            +
                      immediate_parent = parents.reverse[0]
         | 
| 222 | 
            +
                      # This is not currently checking that it is a remote of the same API
         | 
| 223 | 
            +
                      chain << association_name if immediate_parent && immediate_parent.resource.remote
         | 
| 224 | 
            +
                      chain.compact
         | 
| 225 | 
            +
                    end.compact
         | 
| 226 | 
            +
                  else
         | 
| 227 | 
            +
                    parents.map(&:association_name).compact + [association_name].compact
         | 
| 228 | 
            +
                  end
         | 
| 229 | 
            +
                end
         | 
| 230 | 
            +
             | 
| 199 231 | 
             
                private
         | 
| 200 232 |  | 
| 201 233 | 
             
                # Try to find on this resource
         | 
| @@ -204,6 +236,7 @@ module Graphiti | |
| 204 236 | 
             
                # TODO: Eventually, remove the legacy logic
         | 
| 205 237 | 
             
                def validate!(name, flag)
         | 
| 206 238 | 
             
                  return false if name.to_s.include?('.') # nested
         | 
| 239 | 
            +
                  return true if @resource.remote?
         | 
| 207 240 |  | 
| 208 241 | 
             
                  if att = @resource.get_attr(name, flag, request: true)
         | 
| 209 242 | 
             
                    return att
         | 
| @@ -248,7 +281,7 @@ module Graphiti | |
| 248 281 | 
             
                end
         | 
| 249 282 |  | 
| 250 283 | 
             
                def handle_missing_sideload(name)
         | 
| 251 | 
            -
                  if Graphiti.config.raise_on_missing_sideload
         | 
| 284 | 
            +
                  if Graphiti.config.raise_on_missing_sideload && !@resource.remote?
         | 
| 252 285 | 
             
                    raise Graphiti::Errors::InvalidInclude
         | 
| 253 286 | 
             
                      .new(@resource, name)
         | 
| 254 287 | 
             
                  end
         | 
    
        data/lib/graphiti/resource.rb
    CHANGED
    
    
| @@ -35,6 +35,11 @@ module Graphiti | |
| 35 35 | 
             
                      stat total: [:count]
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 | 
            +
                    def remote=(val)
         | 
| 39 | 
            +
                      super
         | 
| 40 | 
            +
                      include ::Graphiti::Resource::Remote
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 38 43 | 
             
                    def model
         | 
| 39 44 | 
             
                      klass = super
         | 
| 40 45 | 
             
                      unless klass || abstract_class?
         | 
| @@ -55,6 +60,8 @@ module Graphiti | |
| 55 60 |  | 
| 56 61 | 
             
                    class_attribute :adapter, instance_reader: false
         | 
| 57 62 | 
             
                    class_attribute :model,
         | 
| 63 | 
            +
                      :remote,
         | 
| 64 | 
            +
                      :remote_base_url,
         | 
| 58 65 | 
             
                      :type,
         | 
| 59 66 | 
             
                      :polymorphic,
         | 
| 60 67 | 
             
                      :polymorphic_child,
         | 
| @@ -65,7 +65,7 @@ module Graphiti | |
| 65 65 | 
             
                    def add_callback(kind, lifecycle, method = nil, only, &blk)
         | 
| 66 66 | 
             
                      config[:callbacks][kind] ||= {}
         | 
| 67 67 | 
             
                      config[:callbacks][kind][lifecycle] ||= []
         | 
| 68 | 
            -
                      config[:callbacks][kind][lifecycle] << { callback: (method || blk), only: only }
         | 
| 68 | 
            +
                      config[:callbacks][kind][lifecycle] << { callback: (method || blk), only: Array(only) }
         | 
| 69 69 | 
             
                    end
         | 
| 70 70 | 
             
                  end
         | 
| 71 71 |  | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            module Graphiti
         | 
| 2 | 
            +
              class Resource
         | 
| 3 | 
            +
                module Remote
         | 
| 4 | 
            +
                  extend ActiveSupport::Concern
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  included do
         | 
| 7 | 
            +
                    self.adapter = Graphiti::Adapters::GraphitiAPI
         | 
| 8 | 
            +
                    self.model = OpenStruct
         | 
| 9 | 
            +
                    self.validate_endpoints = false
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    class_attribute :timeout,
         | 
| 12 | 
            +
                      :open_timeout
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  class_methods do
         | 
| 16 | 
            +
                    def remote_url
         | 
| 17 | 
            +
                      [remote_base_url, remote].join
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def save(*args)
         | 
| 22 | 
            +
                    raise Errors::RemoteWrite.new(self.class)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def destroy(*args)
         | 
| 26 | 
            +
                    raise Errors::RemoteWrite.new(self.class)
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def before_resolve(scope, query)
         | 
| 30 | 
            +
                    scope[:params] = Util::RemoteParams.generate(self, query)
         | 
| 31 | 
            +
                    scope
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  # Forward all headers
         | 
| 35 | 
            +
                  def request_headers
         | 
| 36 | 
            +
                    if defined?(Rails)
         | 
| 37 | 
            +
                      context.request.headers.to_h.reject { |k, v| k.include?('.') }
         | 
| 38 | 
            +
                    else
         | 
| 39 | 
            +
                      {}
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def remote_url
         | 
| 44 | 
            +
                    self.class.remote_url
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  def make_request(url)
         | 
| 48 | 
            +
                    headers = request_headers.dup
         | 
| 49 | 
            +
                    headers['Content-Type'] = 'application/vnd.api+json'
         | 
| 50 | 
            +
                    faraday.get(url, nil, headers) do |req|
         | 
| 51 | 
            +
                      yield req if block_given? # for super do ... end
         | 
| 52 | 
            +
                      req.options.timeout = timeout if timeout
         | 
| 53 | 
            +
                      req.options.open_timeout = open_timeout if open_timeout
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  private
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def faraday
         | 
| 60 | 
            +
                    if defined?(Faraday)
         | 
| 61 | 
            +
                      Faraday
         | 
| 62 | 
            +
                    else
         | 
| 63 | 
            +
                      raise "Faraday not defined. Please require the 'faraday' gem to use remote resources"
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
| @@ -83,12 +83,17 @@ module Graphiti | |
| 83 83 | 
             
                def save(action: :create)
         | 
| 84 84 | 
             
                  # TODO: remove this. Only used for persisting many-to-many with AR
         | 
| 85 85 | 
             
                  # (see activerecord adapter)
         | 
| 86 | 
            -
                  Graphiti.context[:namespace] | 
| 87 | 
            -
                   | 
| 88 | 
            -
                     | 
| 89 | 
            -
             | 
| 90 | 
            -
                      @ | 
| 91 | 
            -
             | 
| 86 | 
            +
                  original = Graphiti.context[:namespace]
         | 
| 87 | 
            +
                  begin
         | 
| 88 | 
            +
                    Graphiti.context[:namespace] = action
         | 
| 89 | 
            +
                    validator = persist do
         | 
| 90 | 
            +
                      @resource.persist_with_relationships \
         | 
| 91 | 
            +
                        @payload.meta(action: action),
         | 
| 92 | 
            +
                        @payload.attributes,
         | 
| 93 | 
            +
                        @payload.relationships
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  ensure
         | 
| 96 | 
            +
                    Graphiti.context[:namespace] = original
         | 
| 92 97 | 
             
                  end
         | 
| 93 98 | 
             
                  @data, success = validator.to_a
         | 
| 94 99 |  | 
    
        data/lib/graphiti/schema.rb
    CHANGED
    
    | @@ -23,6 +23,8 @@ module Graphiti | |
| 23 23 |  | 
| 24 24 | 
             
                def initialize(resources)
         | 
| 25 25 | 
             
                  @resources = resources.sort_by(&:name)
         | 
| 26 | 
            +
                  @remote_resources = resources.select(&:remote?)
         | 
| 27 | 
            +
                  @resources = @resources - @remote_resources
         | 
| 26 28 | 
             
                end
         | 
| 27 29 |  | 
| 28 30 | 
             
                def generate
         | 
| @@ -79,7 +81,7 @@ module Graphiti | |
| 79 81 | 
             
                end
         | 
| 80 82 |  | 
| 81 83 | 
             
                def generate_resources
         | 
| 82 | 
            -
                  @resources.map do |r|
         | 
| 84 | 
            +
                  arr = @resources.map do |r|
         | 
| 83 85 | 
             
                    config = {
         | 
| 84 86 | 
             
                      name: r.name,
         | 
| 85 87 | 
             
                      type: r.type.to_s,
         | 
| @@ -108,6 +110,17 @@ module Graphiti | |
| 108 110 |  | 
| 109 111 | 
             
                    config
         | 
| 110 112 | 
             
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  arr |= @remote_resources.map do |r|
         | 
| 115 | 
            +
                    {
         | 
| 116 | 
            +
                      name: r.name,
         | 
| 117 | 
            +
                      description: r.description,
         | 
| 118 | 
            +
                      remote: r.remote_url,
         | 
| 119 | 
            +
                      relationships: relationships(r)
         | 
| 120 | 
            +
                    }
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  arr
         | 
| 111 124 | 
             
                end
         | 
| 112 125 |  | 
| 113 126 | 
             
                def attributes(resource)
         | 
    
        data/lib/graphiti/scope.rb
    CHANGED
    
    | @@ -18,6 +18,7 @@ module Graphiti | |
| 18 18 | 
             
                    []
         | 
| 19 19 | 
             
                  else
         | 
| 20 20 | 
             
                    resolved = broadcast_data do |payload|
         | 
| 21 | 
            +
                      @object = @resource.before_resolve(@object, @query)
         | 
| 21 22 | 
             
                      payload[:results] = @resource.resolve(@object)
         | 
| 22 23 | 
             
                      payload[:results]
         | 
| 23 24 | 
             
                    end
         | 
| @@ -40,6 +41,7 @@ module Graphiti | |
| 40 41 |  | 
| 41 42 | 
             
                  @query.sideloads.each_pair do |name, q|
         | 
| 42 43 | 
             
                    sideload = @resource.class.sideload(name)
         | 
| 44 | 
            +
                    next if sideload.nil? || sideload.shared_remote?
         | 
| 43 45 | 
             
                    _parent = @resource
         | 
| 44 46 | 
             
                    _context = Graphiti.context
         | 
| 45 47 | 
             
                    resolve_sideload = -> {
         | 
| @@ -94,11 +96,15 @@ module Graphiti | |
| 94 96 |  | 
| 95 97 | 
             
                def apply_scoping(scope, opts)
         | 
| 96 98 | 
             
                  @object = scope
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                   | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 99 | 
            +
             | 
| 100 | 
            +
                  unless @resource.remote?
         | 
| 101 | 
            +
                    opts[:default_paginate] = false unless @query.paginate?
         | 
| 102 | 
            +
                    add_scoping(nil, Graphiti::Scoping::DefaultFilter, opts)
         | 
| 103 | 
            +
                    add_scoping(:filter, Graphiti::Scoping::Filter, opts)
         | 
| 104 | 
            +
                    add_scoping(:sort, Graphiti::Scoping::Sort, opts)
         | 
| 105 | 
            +
                    add_scoping(:paginate, Graphiti::Scoping::Paginate, opts)
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 102 108 | 
             
                  @object
         | 
| 103 109 | 
             
                end
         | 
| 104 110 |  | 
    
        data/lib/graphiti/sideload.rb
    CHANGED
    
    | @@ -11,7 +11,8 @@ module Graphiti | |
| 11 11 | 
             
                  :parent,
         | 
| 12 12 | 
             
                  :group_name,
         | 
| 13 13 | 
             
                  :link,
         | 
| 14 | 
            -
                  :description
         | 
| 14 | 
            +
                  :description,
         | 
| 15 | 
            +
                  :polymorphic_as
         | 
| 15 16 |  | 
| 16 17 | 
             
                class_attribute :scope_proc,
         | 
| 17 18 | 
             
                  :assign_proc,
         | 
| @@ -22,6 +23,7 @@ module Graphiti | |
| 22 23 |  | 
| 23 24 | 
             
                def initialize(name, opts)
         | 
| 24 25 | 
             
                  @name                  = name
         | 
| 26 | 
            +
                  validate_options!(opts)
         | 
| 25 27 | 
             
                  @parent_resource_class = opts[:parent_resource]
         | 
| 26 28 | 
             
                  @resource_class        = opts[:resource]
         | 
| 27 29 | 
             
                  @primary_key           = opts[:primary_key]
         | 
| @@ -33,17 +35,25 @@ module Graphiti | |
| 33 35 | 
             
                  @as                    = opts[:as]
         | 
| 34 36 | 
             
                  @link                  = opts[:link]
         | 
| 35 37 | 
             
                  @single                = opts[:single]
         | 
| 38 | 
            +
                  @remote                = opts[:remote]
         | 
| 36 39 | 
             
                  apply_belongs_to_many_filter if type == :many_to_many
         | 
| 37 40 |  | 
| 38 41 | 
             
                  @description           = opts[:description]
         | 
| 39 42 |  | 
| 40 | 
            -
                  # polymorphic | 
| 43 | 
            +
                  # polymorphic has_many
         | 
| 44 | 
            +
                  @polymorphic_as        = opts[:polymorphic_as]
         | 
| 45 | 
            +
                  # polymorphic_belongs_to-specific
         | 
| 41 46 | 
             
                  @group_name            = opts[:group_name]
         | 
| 42 47 | 
             
                  @polymorphic_child     = opts[:polymorphic_child]
         | 
| 43 48 | 
             
                  @parent                = opts[:parent]
         | 
| 44 49 | 
             
                  if polymorphic_child?
         | 
| 45 50 | 
             
                    parent.resource.polymorphic << resource_class
         | 
| 46 51 | 
             
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  if remote?
         | 
| 54 | 
            +
                    @link = false
         | 
| 55 | 
            +
                    @resource_class = create_remote_resource
         | 
| 56 | 
            +
                  end
         | 
| 47 57 | 
             
                end
         | 
| 48 58 |  | 
| 49 59 | 
             
                def self.scope(&blk)
         | 
| @@ -70,10 +80,27 @@ module Graphiti | |
| 70 80 | 
             
                  self.link_proc = blk
         | 
| 71 81 | 
             
                end
         | 
| 72 82 |  | 
| 83 | 
            +
                def create_remote_resource
         | 
| 84 | 
            +
                  _remote = @remote
         | 
| 85 | 
            +
                  klass = Class.new(Graphiti::Resource) do
         | 
| 86 | 
            +
                    self.adapter = Graphiti::Adapters::GraphitiAPI
         | 
| 87 | 
            +
                    self.model = OpenStruct
         | 
| 88 | 
            +
                    self.remote = _remote
         | 
| 89 | 
            +
                    self.validate_endpoints = false
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                  name = "#{parent_resource_class.name}.#{@name}.remote"
         | 
| 92 | 
            +
                  klass.class_eval("def self.name;'#{name}';end")
         | 
| 93 | 
            +
                  klass
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 73 96 | 
             
                def errors
         | 
| 74 97 | 
             
                  @errors ||= []
         | 
| 75 98 | 
             
                end
         | 
| 76 99 |  | 
| 100 | 
            +
                def remote?
         | 
| 101 | 
            +
                  !!@remote
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 77 104 | 
             
                def readable?
         | 
| 78 105 | 
             
                  !!@readable
         | 
| 79 106 | 
             
                end
         | 
| @@ -86,6 +113,10 @@ module Graphiti | |
| 86 113 | 
             
                  !!@single
         | 
| 87 114 | 
             
                end
         | 
| 88 115 |  | 
| 116 | 
            +
                def polymorphic_has_many?
         | 
| 117 | 
            +
                  !!@polymorphic_as
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 89 120 | 
             
                def link?
         | 
| 90 121 | 
             
                  return true if link_proc
         | 
| 91 122 |  | 
| @@ -96,6 +127,13 @@ module Graphiti | |
| 96 127 | 
             
                  end
         | 
| 97 128 | 
             
                end
         | 
| 98 129 |  | 
| 130 | 
            +
                # The parent resource is a remote,
         | 
| 131 | 
            +
                # AND the sideload is a remote to the same endpoint
         | 
| 132 | 
            +
                def shared_remote?
         | 
| 133 | 
            +
                  resource.remote? &&
         | 
| 134 | 
            +
                    resource.remote_base_url = parent_resource_class.remote_base_url
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
             | 
| 99 137 | 
             
                def polymorphic_parent?
         | 
| 100 138 | 
             
                  resource.polymorphic?
         | 
| 101 139 | 
             
                end
         | 
| @@ -288,6 +326,18 @@ module Graphiti | |
| 288 326 |  | 
| 289 327 | 
             
                private
         | 
| 290 328 |  | 
| 329 | 
            +
                def validate_options!(opts)
         | 
| 330 | 
            +
                  if opts[:remote]
         | 
| 331 | 
            +
                    if opts[:resource]
         | 
| 332 | 
            +
                      raise Errors::SideloadConfig.new(@name, opts[:parent_resource], 'cannot pass :remote and :resource options together')
         | 
| 333 | 
            +
                    end
         | 
| 334 | 
            +
             | 
| 335 | 
            +
                    if opts[:link]
         | 
| 336 | 
            +
                      raise Errors::SideloadConfig.new(@name, opts[:parent_resource], 'remote sideloads do not currently support :link')
         | 
| 337 | 
            +
                    end
         | 
| 338 | 
            +
                  end
         | 
| 339 | 
            +
                end
         | 
| 340 | 
            +
             | 
| 291 341 | 
             
                def apply_belongs_to_many_filter
         | 
| 292 342 | 
             
                  _self = self
         | 
| 293 343 | 
             
                  fk_type = parent_resource_class.attributes[:id][:type]
         | 
| @@ -48,6 +48,16 @@ class Graphiti::Sideload::BelongsTo < Graphiti::Sideload | |
| 48 48 | 
             
              end
         | 
| 49 49 |  | 
| 50 50 | 
             
              def children_for(parent, map)
         | 
| 51 | 
            -
                 | 
| 51 | 
            +
                fk = parent.send(foreign_key)
         | 
| 52 | 
            +
                children = map[fk]
         | 
| 53 | 
            +
                return children if children
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                keys = map.keys
         | 
| 56 | 
            +
                if fk.is_a?(String) && keys[0].is_a?(Integer)
         | 
| 57 | 
            +
                  fk = fk.to_i
         | 
| 58 | 
            +
                elsif fk.is_a?(Integer) && keys[0].is_a?(String)
         | 
| 59 | 
            +
                  fk = fk.to_s
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
                map[fk] || []
         | 
| 52 62 | 
             
              end
         | 
| 53 63 | 
             
            end
         | 
| @@ -21,6 +21,16 @@ class Graphiti::Sideload::HasMany < Graphiti::Sideload | |
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 23 | 
             
              def children_for(parent, map)
         | 
| 24 | 
            -
                 | 
| 24 | 
            +
                pk = parent.send(primary_key)
         | 
| 25 | 
            +
                children = map[pk]
         | 
| 26 | 
            +
                return children if children
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                keys = map.keys
         | 
| 29 | 
            +
                if pk.is_a?(String) && keys[0].is_a?(Integer)
         | 
| 30 | 
            +
                  pk = pk.to_i
         | 
| 31 | 
            +
                elsif pk.is_a?(Integer) && keys[0].is_a?(String)
         | 
| 32 | 
            +
                  pk = pk.to_s
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
                map[pk] || []
         | 
| 25 35 | 
             
              end
         | 
| 26 36 | 
             
            end
         | 
| @@ -83,6 +83,9 @@ class Graphiti::Util::Persistence | |
| 83 83 | 
             
                  attrs[x[:foreign_key]] = nil
         | 
| 84 84 | 
             
                  update_foreign_type(attrs, x, null: true) if x[:is_polymorphic]
         | 
| 85 85 | 
             
                else
         | 
| 86 | 
            +
                  if x[:sideload].polymorphic_has_many?
         | 
| 87 | 
            +
                    attrs[:"#{x[:sideload].polymorphic_as}_type"] = parent_object.class.name
         | 
| 88 | 
            +
                  end
         | 
| 86 89 | 
             
                  attrs[x[:foreign_key]] = parent_object.send(x[:primary_key])
         | 
| 87 90 | 
             
                  update_foreign_type(attrs, x) if x[:is_polymorphic]
         | 
| 88 91 | 
             
                end
         | 
| @@ -0,0 +1,115 @@ | |
| 1 | 
            +
            # Todo: class purpose
         | 
| 2 | 
            +
            module Graphiti
         | 
| 3 | 
            +
              module Util
         | 
| 4 | 
            +
                class RemoteParams
         | 
| 5 | 
            +
                  def self.generate(resource, query)
         | 
| 6 | 
            +
                    new(resource, query).generate
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def initialize(resource, query)
         | 
| 10 | 
            +
                    @resource = resource
         | 
| 11 | 
            +
                    @query = query
         | 
| 12 | 
            +
                    @sorts = []
         | 
| 13 | 
            +
                    @filters = {}
         | 
| 14 | 
            +
                    @fields = {}
         | 
| 15 | 
            +
                    @extra_fields = {}
         | 
| 16 | 
            +
                    @pagination = {}
         | 
| 17 | 
            +
                    @params = {}
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def generate
         | 
| 21 | 
            +
                    if include_hash = @query.include_hash.presence
         | 
| 22 | 
            +
                      @params[:include] = trim_sideloads(include_hash)
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                    collect_params(@query)
         | 
| 25 | 
            +
                    @params[:sort] = @sorts.join(',') if @sorts.present?
         | 
| 26 | 
            +
                    @params[:filter] = @filters if @filters.present?
         | 
| 27 | 
            +
                    @params[:page] = @pagination if @pagination.present?
         | 
| 28 | 
            +
                    @params[:fields] = @fields if @fields.present?
         | 
| 29 | 
            +
                    @params[:extra_fields] = @extra_fields if @extra_fields.present?
         | 
| 30 | 
            +
                    @params[:stats] = @stats if @stats.present?
         | 
| 31 | 
            +
                    @params
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  private
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def collect_params(query)
         | 
| 37 | 
            +
                    query_hash = query.hash
         | 
| 38 | 
            +
                    process_sorts(query_hash[:sort], query)
         | 
| 39 | 
            +
                    process_fields(query_hash[:fields])
         | 
| 40 | 
            +
                    process_extra_fields(query_hash[:extra_fields])
         | 
| 41 | 
            +
                    process_filters(query_hash[:filter], query)
         | 
| 42 | 
            +
                    process_pagination(query_hash[:page], query)
         | 
| 43 | 
            +
                    process_stats(query_hash[:stats])
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    query.sideloads.each_pair do |assn_name, nested_query|
         | 
| 46 | 
            +
                      unless @resource.class.sideload(assn_name)
         | 
| 47 | 
            +
                        collect_params(nested_query)
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def process_stats(stats)
         | 
| 53 | 
            +
                    return unless stats.present?
         | 
| 54 | 
            +
                    @stats = { stats.keys.first => stats.values.join(',') }
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def process_pagination(page, query)
         | 
| 58 | 
            +
                    return unless page.present?
         | 
| 59 | 
            +
                    if size = page[:size]
         | 
| 60 | 
            +
                      key = (query.chain + [:size]).join('.')
         | 
| 61 | 
            +
                      @pagination[key.to_sym] = size
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                    if number = page[:number]
         | 
| 64 | 
            +
                      key = (query.chain + [:number]).join('.')
         | 
| 65 | 
            +
                      @pagination[key.to_sym] = number
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  def process_filters(filters, query)
         | 
| 70 | 
            +
                    return unless filters.present?
         | 
| 71 | 
            +
                    filters.each_pair do |att, config|
         | 
| 72 | 
            +
                      att = (query.chain + [att]).join('.')
         | 
| 73 | 
            +
                      @filters[att.to_sym] = config
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def process_fields(fields)
         | 
| 78 | 
            +
                    return unless fields
         | 
| 79 | 
            +
                    @fields[fields.keys.first.to_sym] = fields.values.join(',')
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def process_extra_fields(fields)
         | 
| 83 | 
            +
                    return unless fields
         | 
| 84 | 
            +
                    @extra_fields[fields.keys.first.to_sym] = fields.values.join(',')
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  def process_sorts(sorts, query)
         | 
| 88 | 
            +
                    return unless sorts
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    if sorts.is_a?(String) # manually assigned
         | 
| 91 | 
            +
                      @sorts << sorts
         | 
| 92 | 
            +
                    else
         | 
| 93 | 
            +
                      sorts.each do |s|
         | 
| 94 | 
            +
                        sort = (query.chain + [s.keys.first]).join('.')
         | 
| 95 | 
            +
                        sort = "-#{sort}" if s.values.first == :desc
         | 
| 96 | 
            +
                        @sorts << sort
         | 
| 97 | 
            +
                      end
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  # Do not pass local sideloads to the remote endpoint
         | 
| 102 | 
            +
                  def trim_sideloads(include_hash)
         | 
| 103 | 
            +
                    return unless include_hash.present?
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    include_hash.each_pair do |assn_name, nested|
         | 
| 106 | 
            +
                      sideload = @resource.class.sideload(assn_name)
         | 
| 107 | 
            +
                      if sideload && !sideload.shared_remote?
         | 
| 108 | 
            +
                        include_hash.delete(assn_name)
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
                    JSONAPI::IncludeDirective.new(include_hash).to_string
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
              end
         | 
| 115 | 
            +
            end
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            module Graphiti
         | 
| 2 | 
            +
              module Util
         | 
| 3 | 
            +
                class RemoteSerializer
         | 
| 4 | 
            +
                  def self.for(base, models)
         | 
| 5 | 
            +
                    new(base).generate(models)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(base)
         | 
| 9 | 
            +
                    @serializer = ::Class.new(base)
         | 
| 10 | 
            +
                    @serializer.type { @object._type }
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def generate(models)
         | 
| 14 | 
            +
                    models.each do |model|
         | 
| 15 | 
            +
                      model.to_h.each_pair do |key, value|
         | 
| 16 | 
            +
                        if key == :_relationships
         | 
| 17 | 
            +
                          add_relationships(value)
         | 
| 18 | 
            +
                        else
         | 
| 19 | 
            +
                          @serializer.attribute(key) if add_attribute?(model, key)
         | 
| 20 | 
            +
                        end
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                    post_process(@serializer, models)
         | 
| 24 | 
            +
                    @serializer
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  private
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def add_relationships(relationship_hash)
         | 
| 30 | 
            +
                    relationship_hash.each_pair do |name, reldata|
         | 
| 31 | 
            +
                      @serializer.relationship(name.to_sym)
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def add_attribute?(model, name)
         | 
| 36 | 
            +
                    disallow = [:_type, :id].include?(name)
         | 
| 37 | 
            +
                    pre_existing = @serializer.attribute_blocks[name]
         | 
| 38 | 
            +
                    is_relationship = model._relationships.try(:[], name.to_s)
         | 
| 39 | 
            +
                    !disallow && !pre_existing && !is_relationship
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def post_process(serializer, models)
         | 
| 43 | 
            +
                    models.each do |model|
         | 
| 44 | 
            +
                      model.delete_field(:_relationships)
         | 
| 45 | 
            +
                      # If this isn't set, Array(resources) will return []
         | 
| 46 | 
            +
                      # This is important, because jsonapi-serializable makes this call
         | 
| 47 | 
            +
                      model.instance_variable_set(:@__graphiti_resource, 1)
         | 
| 48 | 
            +
                      model.instance_variable_set(:@__graphiti_serializer, serializer)
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
    
        data/lib/graphiti/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: graphiti
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0.rc. | 
| 4 | 
            +
              version: 1.0.rc.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Lee Richmond
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019-01- | 
| 11 | 
            +
            date: 2019-01-22 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: jsonapi-serializable
         | 
| @@ -86,6 +86,20 @@ dependencies: | |
| 86 86 | 
             
                - - "<"
         | 
| 87 87 | 
             
                  - !ruby/object:Gem::Version
         | 
| 88 88 | 
             
                    version: '6'
         | 
| 89 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 90 | 
            +
              name: faraday
         | 
| 91 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 92 | 
            +
                requirements:
         | 
| 93 | 
            +
                - - "~>"
         | 
| 94 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 95 | 
            +
                    version: '0.15'
         | 
| 96 | 
            +
              type: :development
         | 
| 97 | 
            +
              prerelease: false
         | 
| 98 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 99 | 
            +
                requirements:
         | 
| 100 | 
            +
                - - "~>"
         | 
| 101 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 102 | 
            +
                    version: '0.15'
         | 
| 89 103 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 90 104 | 
             
              name: activerecord
         | 
| 91 105 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -260,6 +274,7 @@ files: | |
| 260 274 | 
             
            - lib/graphiti/adapters/active_record/has_one_sideload.rb
         | 
| 261 275 | 
             
            - lib/graphiti/adapters/active_record/inferrence.rb
         | 
| 262 276 | 
             
            - lib/graphiti/adapters/active_record/many_to_many_sideload.rb
         | 
| 277 | 
            +
            - lib/graphiti/adapters/graphiti_api.rb
         | 
| 263 278 | 
             
            - lib/graphiti/adapters/null.rb
         | 
| 264 279 | 
             
            - lib/graphiti/base.rb
         | 
| 265 280 | 
             
            - lib/graphiti/cli.rb
         | 
| @@ -286,6 +301,7 @@ files: | |
| 286 301 | 
             
            - lib/graphiti/resource/links.rb
         | 
| 287 302 | 
             
            - lib/graphiti/resource/persistence.rb
         | 
| 288 303 | 
             
            - lib/graphiti/resource/polymorphism.rb
         | 
| 304 | 
            +
            - lib/graphiti/resource/remote.rb
         | 
| 289 305 | 
             
            - lib/graphiti/resource/sideloading.rb
         | 
| 290 306 | 
             
            - lib/graphiti/resource_proxy.rb
         | 
| 291 307 | 
             
            - lib/graphiti/responders.rb
         | 
| @@ -320,6 +336,8 @@ files: | |
| 320 336 | 
             
            - lib/graphiti/util/link.rb
         | 
| 321 337 | 
             
            - lib/graphiti/util/persistence.rb
         | 
| 322 338 | 
             
            - lib/graphiti/util/relationship_payload.rb
         | 
| 339 | 
            +
            - lib/graphiti/util/remote_params.rb
         | 
| 340 | 
            +
            - lib/graphiti/util/remote_serializer.rb
         | 
| 323 341 | 
             
            - lib/graphiti/util/serializer_attributes.rb
         | 
| 324 342 | 
             
            - lib/graphiti/util/serializer_relationships.rb
         | 
| 325 343 | 
             
            - lib/graphiti/util/sideload.rb
         |