jsonapi-consumer 0.1.1 → 1.0.0
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 +5 -5
 - data/.circleci/config.yml +27 -0
 - data/.gitignore +1 -0
 - data/Gemfile +6 -4
 - data/README.md +9 -38
 - data/Rakefile +17 -6
 - data/bin/console +14 -0
 - data/bin/setup +8 -0
 - data/jsonapi-consumer.gemspec +10 -11
 - data/lib/jsonapi/consumer/associations/base_association.rb +26 -0
 - data/lib/jsonapi/consumer/associations/belongs_to.rb +30 -0
 - data/lib/jsonapi/consumer/associations/has_many.rb +26 -0
 - data/lib/jsonapi/consumer/associations/has_one.rb +19 -0
 - data/lib/jsonapi/consumer/connection.rb +36 -0
 - data/lib/jsonapi/consumer/error_collector.rb +91 -0
 - data/lib/jsonapi/consumer/errors.rb +34 -76
 - data/lib/jsonapi/consumer/formatter.rb +145 -0
 - data/lib/jsonapi/consumer/helpers/callbacks.rb +27 -0
 - data/lib/jsonapi/consumer/helpers/dirty.rb +71 -0
 - data/lib/jsonapi/consumer/helpers/dynamic_attributes.rb +83 -0
 - data/lib/jsonapi/consumer/helpers/uri.rb +9 -0
 - data/lib/jsonapi/consumer/implementation.rb +12 -0
 - data/lib/jsonapi/consumer/included_data.rb +49 -0
 - data/lib/jsonapi/consumer/linking/links.rb +22 -0
 - data/lib/jsonapi/consumer/linking/top_level_links.rb +39 -0
 - data/lib/jsonapi/consumer/meta_data.rb +19 -0
 - data/lib/jsonapi/consumer/middleware/json_request.rb +26 -0
 - data/lib/jsonapi/consumer/middleware/parse_json.rb +22 -23
 - data/lib/jsonapi/consumer/middleware/status.rb +41 -0
 - data/lib/jsonapi/consumer/paginating/paginator.rb +89 -0
 - data/lib/jsonapi/consumer/parsers/parser.rb +113 -0
 - data/lib/jsonapi/consumer/query/builder.rb +212 -0
 - data/lib/jsonapi/consumer/query/requestor.rb +67 -0
 - data/lib/jsonapi/consumer/relationships/relations.rb +56 -0
 - data/lib/jsonapi/consumer/relationships/top_level_relations.rb +30 -0
 - data/lib/jsonapi/consumer/resource.rb +514 -54
 - data/lib/jsonapi/consumer/result_set.rb +25 -0
 - data/lib/jsonapi/consumer/schema.rb +153 -0
 - data/lib/jsonapi/consumer/utils.rb +28 -0
 - data/lib/jsonapi/consumer/version.rb +1 -1
 - data/lib/jsonapi/consumer.rb +59 -34
 - metadata +51 -111
 - data/.rspec +0 -2
 - data/CHANGELOG.md +0 -36
 - data/lib/jsonapi/consumer/middleware/raise_error.rb +0 -21
 - data/lib/jsonapi/consumer/middleware/request_headers.rb +0 -20
 - data/lib/jsonapi/consumer/middleware/request_timeout.rb +0 -9
 - data/lib/jsonapi/consumer/middleware.rb +0 -5
 - data/lib/jsonapi/consumer/parser.rb +0 -75
 - data/lib/jsonapi/consumer/query/base.rb +0 -34
 - data/lib/jsonapi/consumer/query/create.rb +0 -9
 - data/lib/jsonapi/consumer/query/delete.rb +0 -10
 - data/lib/jsonapi/consumer/query/find.rb +0 -16
 - data/lib/jsonapi/consumer/query/new.rb +0 -15
 - data/lib/jsonapi/consumer/query/update.rb +0 -11
 - data/lib/jsonapi/consumer/query.rb +0 -5
 - data/lib/jsonapi/consumer/resource/association_concern.rb +0 -203
 - data/lib/jsonapi/consumer/resource/attributes_concern.rb +0 -70
 - data/lib/jsonapi/consumer/resource/connection_concern.rb +0 -99
 - data/lib/jsonapi/consumer/resource/finders_concern.rb +0 -28
 - data/lib/jsonapi/consumer/resource/object_build_concern.rb +0 -28
 - data/lib/jsonapi/consumer/resource/serializer_concern.rb +0 -63
 - data/spec/fixtures/.gitkeep +0 -0
 - data/spec/fixtures/resources.rb +0 -45
 - data/spec/fixtures/responses.rb +0 -64
 - data/spec/jsonapi/consumer/associations_spec.rb +0 -166
 - data/spec/jsonapi/consumer/attributes_spec.rb +0 -27
 - data/spec/jsonapi/consumer/connection_spec.rb +0 -147
 - data/spec/jsonapi/consumer/error_handling_spec.rb +0 -37
 - data/spec/jsonapi/consumer/object_build_spec.rb +0 -20
 - data/spec/jsonapi/consumer/parser_spec.rb +0 -39
 - data/spec/jsonapi/consumer/resource_spec.rb +0 -62
 - data/spec/jsonapi/consumer/serializer_spec.rb +0 -41
 - data/spec/spec_helper.rb +0 -97
 - data/spec/support/.gitkeep +0 -0
 - data/spec/support/load_fixtures.rb +0 -4
 
| 
         @@ -0,0 +1,113 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module JSONAPI::Consumer
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Parsers
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Parser
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 5 
     | 
    
         
            +
                    def parse(klass, response)
         
     | 
| 
      
 6 
     | 
    
         
            +
                      data = response.body.present? ? response.body : {}
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                      ResultSet.new.tap do |result_set|
         
     | 
| 
      
 9 
     | 
    
         
            +
                        result_set.record_class = klass
         
     | 
| 
      
 10 
     | 
    
         
            +
                        result_set.uri = response.env[:url]
         
     | 
| 
      
 11 
     | 
    
         
            +
                        handle_json_api(result_set, data)
         
     | 
| 
      
 12 
     | 
    
         
            +
                        handle_data(result_set, data)
         
     | 
| 
      
 13 
     | 
    
         
            +
                        handle_errors(result_set, data)
         
     | 
| 
      
 14 
     | 
    
         
            +
                        handle_meta(result_set, data)
         
     | 
| 
      
 15 
     | 
    
         
            +
                        handle_links(result_set, data)
         
     | 
| 
      
 16 
     | 
    
         
            +
                        handle_relationships(result_set, data)
         
     | 
| 
      
 17 
     | 
    
         
            +
                        handle_pagination(result_set, data)
         
     | 
| 
      
 18 
     | 
    
         
            +
                        handle_included(result_set, data)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      end
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    #
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # Given a resource hash, returns a Resource.new friendly hash
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # which flattens the attributes in w/ id and type.
         
     | 
| 
      
 25 
     | 
    
         
            +
                    #
         
     | 
| 
      
 26 
     | 
    
         
            +
                    # Example:
         
     | 
| 
      
 27 
     | 
    
         
            +
                    #
         
     | 
| 
      
 28 
     | 
    
         
            +
                    # Given:
         
     | 
| 
      
 29 
     | 
    
         
            +
                    #  {
         
     | 
| 
      
 30 
     | 
    
         
            +
                    #    id: 1.
         
     | 
| 
      
 31 
     | 
    
         
            +
                    #    type: 'person',
         
     | 
| 
      
 32 
     | 
    
         
            +
                    #    attributes: {
         
     | 
| 
      
 33 
     | 
    
         
            +
                    #      first_name: 'Jeff',
         
     | 
| 
      
 34 
     | 
    
         
            +
                    #      last_name: 'Ching'
         
     | 
| 
      
 35 
     | 
    
         
            +
                    #    },
         
     | 
| 
      
 36 
     | 
    
         
            +
                    #    links: {...},
         
     | 
| 
      
 37 
     | 
    
         
            +
                    #    relationships: {...}
         
     | 
| 
      
 38 
     | 
    
         
            +
                    #  }
         
     | 
| 
      
 39 
     | 
    
         
            +
                    #
         
     | 
| 
      
 40 
     | 
    
         
            +
                    # Returns:
         
     | 
| 
      
 41 
     | 
    
         
            +
                    #  {
         
     | 
| 
      
 42 
     | 
    
         
            +
                    #    id: 1,
         
     | 
| 
      
 43 
     | 
    
         
            +
                    #    type: 'person',
         
     | 
| 
      
 44 
     | 
    
         
            +
                    #    first_name: 'Jeff',
         
     | 
| 
      
 45 
     | 
    
         
            +
                    #    last_name: 'Ching'
         
     | 
| 
      
 46 
     | 
    
         
            +
                    #    links: {...},
         
     | 
| 
      
 47 
     | 
    
         
            +
                    #    relationships: {...}
         
     | 
| 
      
 48 
     | 
    
         
            +
                    #  }
         
     | 
| 
      
 49 
     | 
    
         
            +
                    #
         
     | 
| 
      
 50 
     | 
    
         
            +
                    #
         
     | 
| 
      
 51 
     | 
    
         
            +
                    def parameters_from_resource(params)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      attrs = params.slice('id', 'links', 'meta', 'type', 'relationships')
         
     | 
| 
      
 53 
     | 
    
         
            +
                      attrs.merge(params.fetch('attributes', {}))
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    private
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    def handle_json_api(result_set, data)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      result_set.implementation = Implementation.new(data.fetch("jsonapi", {}))
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    def handle_data(result_set, data)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      # all data lives under the "data" attribute
         
     | 
| 
      
 64 
     | 
    
         
            +
                      results = data.fetch("data", [])
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                      # we will treat everything as an Array
         
     | 
| 
      
 67 
     | 
    
         
            +
                      results = [results] unless results.is_a?(Array)
         
     | 
| 
      
 68 
     | 
    
         
            +
                      resources = results.compact.map do |res|
         
     | 
| 
      
 69 
     | 
    
         
            +
                        record_class = choose_model_for(result_set, res)
         
     | 
| 
      
 70 
     | 
    
         
            +
                        resource = record_class.load(parameters_from_resource(res))
         
     | 
| 
      
 71 
     | 
    
         
            +
                        resource.last_result_set = result_set
         
     | 
| 
      
 72 
     | 
    
         
            +
                        resource
         
     | 
| 
      
 73 
     | 
    
         
            +
                      end
         
     | 
| 
      
 74 
     | 
    
         
            +
                      result_set.concat(resources)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    # Accept mixed-content from an endpoint.
         
     | 
| 
      
 78 
     | 
    
         
            +
                    #
         
     | 
| 
      
 79 
     | 
    
         
            +
                    # TODO: add ability to configure a model namespace
         
     | 
| 
      
 80 
     | 
    
         
            +
                    def choose_model_for(result_set, res)
         
     | 
| 
      
 81 
     | 
    
         
            +
                      return result_set.record_class unless res['type']
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                      res_type_name = res['type'].underscore.classify
         
     | 
| 
      
 84 
     | 
    
         
            +
                      (res_type_name.safe_constantize) ? res_type_name.safe_constantize : result_set.record_class
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    def handle_errors(result_set, data)
         
     | 
| 
      
 88 
     | 
    
         
            +
                      result_set.errors = ErrorCollector.new(data.fetch("errors", []))
         
     | 
| 
      
 89 
     | 
    
         
            +
                    end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    def handle_meta(result_set, data)
         
     | 
| 
      
 92 
     | 
    
         
            +
                      result_set.meta = MetaData.new(data.fetch("meta", {}), result_set.record_class)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    def handle_links(result_set, data)
         
     | 
| 
      
 96 
     | 
    
         
            +
                      result_set.links = Linking::TopLevelLinks.new(result_set.record_class, data.fetch("links", {}))
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    def handle_relationships(result_set, data)
         
     | 
| 
      
 100 
     | 
    
         
            +
                      result_set.relationships = Relationships::TopLevelRelations.new(result_set.record_class, data.fetch("relationships", {}))
         
     | 
| 
      
 101 
     | 
    
         
            +
                    end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    def handle_pagination(result_set, data)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      result_set.pages = result_set.record_class.paginator.new(result_set, data)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                    def handle_included(result_set, data)
         
     | 
| 
      
 108 
     | 
    
         
            +
                      result_set.included = IncludedData.new(result_set, data.fetch("included", []))
         
     | 
| 
      
 109 
     | 
    
         
            +
                    end
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
              end
         
     | 
| 
      
 113 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,212 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module JSONAPI::Consumer
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Query
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Builder
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_reader :klass, :requestor
         
     | 
| 
      
 6 
     | 
    
         
            +
                  delegate :key_formatter, to: :klass
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def initialize(klass, requestor = nil)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @klass = klass
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @requestor = requestor || klass.requestor
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @primary_key = nil
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @pagination_params = {}
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @path_params = {}
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @additional_params = {}
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @filters = {}
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @includes = []
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @orders = []
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @fields = []
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def where(conditions = {})
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # pull out any path params here
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @path_params.merge!(conditions.slice(*klass.prefix_params))
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @filters.merge!(conditions.except(*klass.prefix_params))
         
     | 
| 
      
 25 
     | 
    
         
            +
                    self
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def order(*args)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @orders += parse_orders(*args)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    self
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def includes(*tables)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @includes += parse_related_links(*tables)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    self
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def select(*fields)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @fields += parse_fields(*fields)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    self
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def paginate(conditions = {})
         
     | 
| 
      
 44 
     | 
    
         
            +
                    scope = self
         
     | 
| 
      
 45 
     | 
    
         
            +
                    scope = scope.page(conditions[:page]) if conditions[:page]
         
     | 
| 
      
 46 
     | 
    
         
            +
                    scope = scope.per(conditions[:per_page]) if conditions[:per_page]
         
     | 
| 
      
 47 
     | 
    
         
            +
                    scope
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def page(number)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @pagination_params[ klass.paginator.page_param ] = number
         
     | 
| 
      
 52 
     | 
    
         
            +
                    self
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def per(size)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @pagination_params[ klass.paginator.per_page_param ] = size
         
     | 
| 
      
 57 
     | 
    
         
            +
                    self
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def with_params(more_params)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    @additional_params.merge!(more_params)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    self
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  def first
         
     | 
| 
      
 66 
     | 
    
         
            +
                    paginate(page: 1, per_page: 1).to_a.first
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def last
         
     | 
| 
      
 70 
     | 
    
         
            +
                    paginate(page: 1, per_page: 1).pages.last.to_a.last
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  def build
         
     | 
| 
      
 74 
     | 
    
         
            +
                    klass.new(params)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  def params
         
     | 
| 
      
 78 
     | 
    
         
            +
                    filter_params
         
     | 
| 
      
 79 
     | 
    
         
            +
                      .merge(pagination_params)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      .merge(includes_params)
         
     | 
| 
      
 81 
     | 
    
         
            +
                      .merge(order_params)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      .merge(select_params)
         
     | 
| 
      
 83 
     | 
    
         
            +
                      .merge(primary_key_params)
         
     | 
| 
      
 84 
     | 
    
         
            +
                      .merge(path_params)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      .merge(additional_params)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  def to_a
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @to_a ||= find
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
                  alias all to_a
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  def find(args = {})
         
     | 
| 
      
 94 
     | 
    
         
            +
                    case args
         
     | 
| 
      
 95 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 96 
     | 
    
         
            +
                      where(args)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    else
         
     | 
| 
      
 98 
     | 
    
         
            +
                      @primary_key = args
         
     | 
| 
      
 99 
     | 
    
         
            +
                    end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    requestor.get(params)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  def method_missing(method_name, *args, &block)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    to_a.send(method_name, *args, &block)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  private
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  def path_params
         
     | 
| 
      
 111 
     | 
    
         
            +
                    @path_params.empty? ? {} : {path: @path_params}
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  def additional_params
         
     | 
| 
      
 115 
     | 
    
         
            +
                    @additional_params
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  def primary_key_params
         
     | 
| 
      
 119 
     | 
    
         
            +
                    return {} unless @primary_key
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    @primary_key.is_a?(Array) ?
         
     | 
| 
      
 122 
     | 
    
         
            +
                      {klass.primary_key.to_s.pluralize.to_sym => @primary_key.join(",")} :
         
     | 
| 
      
 123 
     | 
    
         
            +
                      {klass.primary_key => @primary_key}
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                  def pagination_params
         
     | 
| 
      
 127 
     | 
    
         
            +
                    @pagination_params.empty? ? {} : {page: @pagination_params}
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  def includes_params
         
     | 
| 
      
 131 
     | 
    
         
            +
                    @includes.empty? ? {} : {include: @includes.join(",")}
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  def filter_params
         
     | 
| 
      
 135 
     | 
    
         
            +
                    @filters.empty? ? {} : {filter: @filters}
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  def order_params
         
     | 
| 
      
 139 
     | 
    
         
            +
                    @orders.empty? ? {} : {sort: @orders.join(",")}
         
     | 
| 
      
 140 
     | 
    
         
            +
                  end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                  def select_params
         
     | 
| 
      
 143 
     | 
    
         
            +
                    if @fields.empty?
         
     | 
| 
      
 144 
     | 
    
         
            +
                      {}
         
     | 
| 
      
 145 
     | 
    
         
            +
                    else
         
     | 
| 
      
 146 
     | 
    
         
            +
                      field_result = Hash.new { |h,k| h[k] = [] }
         
     | 
| 
      
 147 
     | 
    
         
            +
                      @fields.each do |field|
         
     | 
| 
      
 148 
     | 
    
         
            +
                        if field.is_a? Hash
         
     | 
| 
      
 149 
     | 
    
         
            +
                          field.each do |k,v|
         
     | 
| 
      
 150 
     | 
    
         
            +
                            field_result[k.to_s] << v
         
     | 
| 
      
 151 
     | 
    
         
            +
                            field_result[k.to_s] = field_result[k.to_s].flatten
         
     | 
| 
      
 152 
     | 
    
         
            +
                          end
         
     | 
| 
      
 153 
     | 
    
         
            +
                        else
         
     | 
| 
      
 154 
     | 
    
         
            +
                          field_result[klass.table_name] << field
         
     | 
| 
      
 155 
     | 
    
         
            +
                        end
         
     | 
| 
      
 156 
     | 
    
         
            +
                      end
         
     | 
| 
      
 157 
     | 
    
         
            +
                      field_result.each { |k,v| field_result[k] = v.join(',') }
         
     | 
| 
      
 158 
     | 
    
         
            +
                      {fields: field_result}
         
     | 
| 
      
 159 
     | 
    
         
            +
                    end
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                  def parse_related_links(*tables)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    tables.map do |table|
         
     | 
| 
      
 164 
     | 
    
         
            +
                      case table
         
     | 
| 
      
 165 
     | 
    
         
            +
                      when Hash
         
     | 
| 
      
 166 
     | 
    
         
            +
                        table.map do |k, v|
         
     | 
| 
      
 167 
     | 
    
         
            +
                          parse_related_links(*v).map do |sub|
         
     | 
| 
      
 168 
     | 
    
         
            +
                            "#{k}.#{sub}"
         
     | 
| 
      
 169 
     | 
    
         
            +
                          end
         
     | 
| 
      
 170 
     | 
    
         
            +
                        end
         
     | 
| 
      
 171 
     | 
    
         
            +
                      when Array
         
     | 
| 
      
 172 
     | 
    
         
            +
                        table.map do |v|
         
     | 
| 
      
 173 
     | 
    
         
            +
                          parse_related_links(*v)
         
     | 
| 
      
 174 
     | 
    
         
            +
                        end
         
     | 
| 
      
 175 
     | 
    
         
            +
                      else
         
     | 
| 
      
 176 
     | 
    
         
            +
                        key_formatter.format(table)
         
     | 
| 
      
 177 
     | 
    
         
            +
                      end
         
     | 
| 
      
 178 
     | 
    
         
            +
                    end.flatten
         
     | 
| 
      
 179 
     | 
    
         
            +
                  end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                  def parse_orders(*args)
         
     | 
| 
      
 182 
     | 
    
         
            +
                    args.map do |arg|
         
     | 
| 
      
 183 
     | 
    
         
            +
                      case arg
         
     | 
| 
      
 184 
     | 
    
         
            +
                      when Hash
         
     | 
| 
      
 185 
     | 
    
         
            +
                        arg.map do |k, v|
         
     | 
| 
      
 186 
     | 
    
         
            +
                          operator = (v == :desc ? "-" : "")
         
     | 
| 
      
 187 
     | 
    
         
            +
                          "#{operator}#{k}"
         
     | 
| 
      
 188 
     | 
    
         
            +
                        end
         
     | 
| 
      
 189 
     | 
    
         
            +
                      else
         
     | 
| 
      
 190 
     | 
    
         
            +
                        "#{arg}"
         
     | 
| 
      
 191 
     | 
    
         
            +
                      end
         
     | 
| 
      
 192 
     | 
    
         
            +
                    end.flatten
         
     | 
| 
      
 193 
     | 
    
         
            +
                  end
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                  def parse_fields(*fields)
         
     | 
| 
      
 196 
     | 
    
         
            +
                    fields = fields.split(',') if fields.is_a? String
         
     | 
| 
      
 197 
     | 
    
         
            +
                    fields.map do |field|
         
     | 
| 
      
 198 
     | 
    
         
            +
                      case field
         
     | 
| 
      
 199 
     | 
    
         
            +
                      when Hash
         
     | 
| 
      
 200 
     | 
    
         
            +
                        field.each do |k,v|
         
     | 
| 
      
 201 
     | 
    
         
            +
                          field[k] = parse_fields(v)
         
     | 
| 
      
 202 
     | 
    
         
            +
                        end
         
     | 
| 
      
 203 
     | 
    
         
            +
                        field
         
     | 
| 
      
 204 
     | 
    
         
            +
                      else
         
     | 
| 
      
 205 
     | 
    
         
            +
                        Array(field).flatten.map { |i| i.to_s.split(",") }.flatten.map(&:strip)
         
     | 
| 
      
 206 
     | 
    
         
            +
                      end
         
     | 
| 
      
 207 
     | 
    
         
            +
                    end.flatten
         
     | 
| 
      
 208 
     | 
    
         
            +
                  end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                end
         
     | 
| 
      
 211 
     | 
    
         
            +
              end
         
     | 
| 
      
 212 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module JSONAPI::Consumer
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Query
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Requestor
         
     | 
| 
      
 4 
     | 
    
         
            +
                  extend Forwardable
         
     | 
| 
      
 5 
     | 
    
         
            +
                  include Helpers::URI
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(klass, path = nil)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @klass = klass
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @path = path
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  # expects a record
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def create(record)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    request(:post, klass.path(record.attributes), {
         
     | 
| 
      
 15 
     | 
    
         
            +
                      data: record.as_json_api
         
     | 
| 
      
 16 
     | 
    
         
            +
                    })
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def update(record)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    request(:patch, resource_path(record.attributes), {
         
     | 
| 
      
 21 
     | 
    
         
            +
                      data: record.as_json_api
         
     | 
| 
      
 22 
     | 
    
         
            +
                    })
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def get(params = {})
         
     | 
| 
      
 26 
     | 
    
         
            +
                    path = resource_path(params)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    params.delete(klass.primary_key)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    request(:get, path, params)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  def destroy(record)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    request(:delete, resource_path(record.attributes), {})
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def linked(path)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    request(:get, path, {})
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def custom(method_name, options, params)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    path = resource_path(params)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    params.delete(klass.primary_key)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    path = File.join(path, method_name.to_s)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    request(options.fetch(:request_method, :get), path, params)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  attr_reader :klass, :path
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def_delegators :klass, :connection
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def resource_path(parameters)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    base_path = path || klass.path(parameters)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    if resource_id = parameters[klass.primary_key]
         
     | 
| 
      
 55 
     | 
    
         
            +
                      File.join(base_path, encode_part(resource_id))
         
     | 
| 
      
 56 
     | 
    
         
            +
                    else
         
     | 
| 
      
 57 
     | 
    
         
            +
                      base_path
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  def request(type, path, params)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    klass.parser.parse(klass, connection.run(type, path, params, klass.custom_headers))
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module JSONAPI::Consumer
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Relationships
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Relations
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include Helpers::DynamicAttributes
         
     | 
| 
      
 5 
     | 
    
         
            +
                  include Helpers::Dirty
         
     | 
| 
      
 6 
     | 
    
         
            +
                  include ActiveModel::Serialization
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_reader :record_class
         
     | 
| 
      
 9 
     | 
    
         
            +
                  delegate :key_formatter, to: :record_class
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def initialize(record_class, relations)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @record_class = record_class
         
     | 
| 
      
 13 
     | 
    
         
            +
                    self.attributes = relations
         
     | 
| 
      
 14 
     | 
    
         
            +
                    clear_changes_information
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def present?
         
     | 
| 
      
 18 
     | 
    
         
            +
                    attributes.present?
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def as_json_api
         
     | 
| 
      
 22 
     | 
    
         
            +
                    Hash[attributes_for_serialization.map do |k, v|
         
     | 
| 
      
 23 
     | 
    
         
            +
                           [k, v.slice("data")]  if v.has_key?("data")
         
     | 
| 
      
 24 
     | 
    
         
            +
                         end.compact]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def as_json
         
     | 
| 
      
 28 
     | 
    
         
            +
                    Hash[attributes.map do |k, v|
         
     | 
| 
      
 29 
     | 
    
         
            +
                           [k, v.slice("data")]  if v.has_key?("data")
         
     | 
| 
      
 30 
     | 
    
         
            +
                         end.compact]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def attributes_for_serialization
         
     | 
| 
      
 34 
     | 
    
         
            +
                    attributes.slice(*changed)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def set_attribute(name, value)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    value = case value
         
     | 
| 
      
 41 
     | 
    
         
            +
                    when JSONAPI::Consumer::Resource
         
     | 
| 
      
 42 
     | 
    
         
            +
                      {data: value.as_relation}
         
     | 
| 
      
 43 
     | 
    
         
            +
                    when Array
         
     | 
| 
      
 44 
     | 
    
         
            +
                      {data: value.map(&:as_relation)}
         
     | 
| 
      
 45 
     | 
    
         
            +
                    when NilClass
         
     | 
| 
      
 46 
     | 
    
         
            +
                      {data: nil}
         
     | 
| 
      
 47 
     | 
    
         
            +
                    else
         
     | 
| 
      
 48 
     | 
    
         
            +
                      value
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    attribute_will_change!(name) if value != attributes[name]
         
     | 
| 
      
 51 
     | 
    
         
            +
                    attributes[name] = value
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module JSONAPI::Consumer
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Relationships
         
     | 
| 
      
 3 
     | 
    
         
            +
                class TopLevelRelations
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_reader :relations, :record_class
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(record_class, relations)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @relations = relations
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @record_class = record_class
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def respond_to_missing?(method, include_private = false)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    relations.has_key?(method.to_s) || super
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def method_missing(method, *args)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    if respond_to_missing?(method)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      fetch_relation(method)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    else
         
     | 
| 
      
 20 
     | 
    
         
            +
                      super
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def fetch_relation(relation_name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    link_definition = relations.fetch(relation_name.to_s)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    record_class.requestor.linked(link_definition)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     |