graphiti-activegraph 0.2.0 → 1.1.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/specs.yml +17 -3
  3. data/.gitignore +3 -0
  4. data/CHANGELOG.md +22 -67
  5. data/CHANGELOG_PRE_1.0.0.md +70 -0
  6. data/README.md +81 -0
  7. data/graphiti-activegraph.gemspec +4 -0
  8. data/lib/graphiti/active_graph/adapters/active_graph.rb +1 -1
  9. data/lib/graphiti/active_graph/extensions/context.rb +17 -0
  10. data/lib/graphiti/active_graph/extensions/resources/authorizationable.rb +29 -0
  11. data/lib/graphiti/active_graph/extensions/resources/payload_combinable.rb +24 -0
  12. data/lib/graphiti/active_graph/extensions/resources/preloadable.rb +19 -0
  13. data/lib/graphiti/active_graph/extensions/resources/rel.rb +19 -0
  14. data/lib/graphiti/active_graph/jsonapi_ext/include_directive.rb +66 -0
  15. data/lib/graphiti/active_graph/query.rb +27 -0
  16. data/lib/graphiti/active_graph/resource.rb +45 -20
  17. data/lib/graphiti/active_graph/{resource → resources}/interface.rb +1 -1
  18. data/lib/graphiti/active_graph/{resource → resources}/persistence.rb +1 -1
  19. data/lib/graphiti/active_graph/scope.rb +28 -0
  20. data/lib/graphiti/active_graph/scoping/association_eager_load.rb +34 -0
  21. data/lib/graphiti/active_graph/scoping/include.rb +43 -0
  22. data/lib/graphiti/active_graph/scoping/internal/include_normalizer.rb +82 -0
  23. data/lib/graphiti/active_graph/scoping/internal/path_descriptor.rb +94 -0
  24. data/lib/graphiti/active_graph/scoping/internal/sort_normalizer.rb +54 -0
  25. data/lib/graphiti/active_graph/scoping/internal/sorting_aliases.rb +25 -0
  26. data/lib/graphiti/active_graph/scoping/internal/sparse_fields_eagerloading.rb +28 -0
  27. data/lib/graphiti/active_graph/util/parsers/rel_chain.rb +27 -0
  28. data/lib/graphiti/active_graph/util/transformers/relation_param.rb +56 -0
  29. data/lib/graphiti/active_graph/version.rb +1 -1
  30. data/lib/graphiti/sidepost_configuration.rb +1 -2
  31. data/lib/graphiti-activegraph.rb +6 -4
  32. metadata +79 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba0c3b914198cabc31276a535e428887a5ff15ff557d3dbf1f40f7ef63d463ce
4
- data.tar.gz: 29c6bcff84e546779872c7cd197727f8ecaad1a12ece312a5032e93cd90c3060
3
+ metadata.gz: c387f138fe40ab7a304c5d587a0281258e2bf4e8304d7e0f0967295adeeef6d7
4
+ data.tar.gz: 6154b76796fef327e488b722d1075b05f448be9fd499e4b8525e58b3544399d0
5
5
  SHA512:
6
- metadata.gz: 5d930274a4203b73d77a50281c1091e4a4e5cf0bb0b467398dab4b3e2edd59daa60e63d31250941f1c373d04e9136a46bcb2c80b7cd2a0b29dfd685691e4c64f
7
- data.tar.gz: bb3211192ac37a29c578ab71076e57995ddb7768f233a571428c9088989e8b773d36536f6a8f1d9f4d13154c0bfe53aedd3e5916507451641aded67b83f7d36a
6
+ metadata.gz: 16cf1d53448e132be9dd3c45dc755f933f434114dc13d7414b3670becaf97c4696ba0c57e3f10075f984b60516d1286d3ccee01bad8baa4bc2bad1143ea1d1f4
7
+ data.tar.gz: 69a336f08b80474be83a0d0111e74a7cfbed451e5dc3ca2dba41318e3212c0e10565f799be327fdc9a26eed111b7ef1a7a46d90f24e85151a4f0d1e719b909d9
@@ -18,20 +18,34 @@ jobs:
18
18
  strategy:
19
19
  fail-fast: false
20
20
  matrix:
21
- ruby-version: [ jruby, ruby ]
21
+ ruby-version: [ ruby-3.4.2, ruby-3.3.7, ruby-3.2.7 ] # Removed 'jruby' till https://github.com/jruby/jruby/issues/8642 is fixed
22
+ neo4j: [ 5.23.0 ]
23
+ include:
24
+ - ruby: jruby
25
+ java-version: 17
22
26
  env:
23
27
  JRUBY_OPTS: --debug -J-Xmx1280m -Xcompile.invokedynamic=false -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -J-noverify -Xcompile.mode=OFF
24
28
  steps:
29
+ - name: Start neo4j
30
+ run: docker run --name neo4j --env NEO4J_AUTH=neo4j/password --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes --env NEO4J_dbms_directories_import= -p7687:7687 -p7474:7474 -v `pwd`/tmp:/var/lib/neo4j/import --rm neo4j:${{ matrix.neo4j }}-enterprise &
31
+
25
32
  - uses: actions/checkout@v3
33
+
26
34
  - name: Set up Ruby
27
35
  uses: ruby/setup-ruby@v1
28
36
  with:
29
37
  ruby-version: ${{ matrix.ruby-version }}
30
38
  bundler-cache: true
39
+
31
40
  - name: Set up Java
32
- uses: actions/setup-java@v3
41
+ uses: actions/setup-java@v4
42
+ if: matrix.java-version
33
43
  with:
34
44
  distribution: 'temurin'
35
- java-version: 17
45
+ java-version: ${{ matrix.java-version }}
46
+
47
+ - name: Wait for neo4j
48
+ run: while [ $((curl localhost:7474/ > /dev/null 2>&1); echo $?) -ne 0 ]; do sleep 1; done
49
+
36
50
  - name: Run tests
37
51
  run: bundle exec rspec
data/.gitignore CHANGED
@@ -52,5 +52,8 @@ build-iPhoneSimulator/
52
52
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
53
  .rvmrc
54
54
 
55
+ # RubyMine project files
56
+ .idea
57
+
55
58
  # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56
59
  # .rubocop-https?--*
data/CHANGELOG.md CHANGED
@@ -1,80 +1,35 @@
1
- ## Unreleased
1
+ # Changelog
2
2
 
3
- -
3
+ All notable changes to this project will be documented in this file.
4
4
 
5
- ## 0.1.8 (09-06-2021)
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
7
 
7
- Features:
8
+ ## [Unreleased]
8
9
 
9
- - Added support for polymorphic relationship.
10
+ ## [1.1.0] - 2025-05-2
10
11
 
11
- ## 0.1.13 (21-12-2021)
12
+ ### Added
12
13
 
13
- Features:
14
+ - **Deep sideloading**: introduced Include scoping class to support deep sideloading using single query (e.g. include='author.posts')
15
+ - **Deep sorting**: added SortNormalizer to support sorting by deep sideloaded resource's attribute (e.g. sort='author.posts.title')
16
+ - **Links control**: Control the links(pagination and resource) in response via request query params (pagination_links=true|false, links=true|false)
14
17
 
15
- - Supports Jruby-9.3.2.0
18
+ ## [1.0.0] - 2025-03-18
16
19
 
17
- ## 0.1.14 (21-12-2021)
20
+ ### Added
18
21
 
19
- Features:
22
+ - **MRI Support**: Introduced compatibility with MRI (Matz's Ruby Interpreter), expanding the gem's usability beyond JRuby (#38).
23
+ - **Documentation Updates**: Expanded the README to provide clearer guidance on gem usage and integration (#38).
24
+ - **Sideloading Workflow**: Enhanced scoping mechanisms and added support for eager loading associations to improve data retrieval efficiency and flexibility (#38).
20
25
 
21
- - Adding unpaginated query to resource proxy with preloaded records. This will help in getting count on API.
26
+ ### Changed
27
+ - **Resource Class**: Instead of modifying the `Graphiti::Resource` class, we now define `Graphiti::ActiveGraph::Resource`,
28
+ which must be inherited in all resource classes to enable ActiveGraph support (#38).
22
29
 
23
- ## 0.1.15 (21-06-2022)
30
+ ---
24
31
 
25
- Features:
32
+ *Note: For details on changes prior to version 1.0.0, please refer to the [`CHANGELOG_PRE_1.0.0.md`](CHANGELOG_PRE_1.0.0.md) file.*
26
33
 
27
- - Relationships mentioned in sparse field param (and not in include), will now be returned in relationship block of response
28
-
29
- ## 0.1.20
30
-
31
- Features:
32
-
33
- - With graphiti config variable "allow_sidepost" you can allow/disallow sideposting, by default it is allowed.
34
-
35
- ## 0.1.21
36
-
37
- Fixes:
38
-
39
- - Runner#proxy keyword arguments
40
-
41
- ## 0.1.22
42
-
43
- Fixes:
44
-
45
- - when rendering preloaded resources, we were not applying scoping. Now we are skipping around_scoping callback too.
46
-
47
- ## 0.1.23 (29-04-2024)
48
-
49
- Features:
50
-
51
- - Added support for UUID
52
-
53
- ## 0.1.24 (18-06-2024)
54
-
55
- Features:
56
-
57
- - Added preliminary support for Sideload backed by function instead of model association
58
-
59
- ## 0.1.25 (04-12-2024)
60
-
61
- Features:
62
-
63
- - Added support to preload extra_fields for the main resource, replacing N+1 queries with a single query. This does not apply to sideloaded resources.
64
-
65
- ## 0.2.0 (01-24-2025)
66
-
67
- Features:
68
-
69
- - Added MRI support
70
- - Added support for rails 8
71
-
72
- Breaking changes:
73
-
74
- - Removed support for graphiti <= 1.6.3
75
-
76
- <!-- ### [version (DD-MM-YYYY)] -->
77
- <!-- Breaking changes:-->
78
- <!-- Features:-->
79
- <!-- Fixes:-->
80
- <!-- Misc:-->
34
+ [unreleased]: https://github.com/mrhardikjoshi/graphiti-activegraph/compare/v1.0.0...master
35
+ [1.0.0]: https://github.com/mrhardikjoshi/graphiti-activegraph/compare/9f837108ae57287c65b0f6fd2609dd56a95cd461...v1.0.0
@@ -0,0 +1,70 @@
1
+ ## 0.1.8 (09-06-2021)
2
+
3
+ Features:
4
+
5
+ - Added support for polymorphic relationship.
6
+
7
+ ## 0.1.13 (21-12-2021)
8
+
9
+ Features:
10
+
11
+ - Supports Jruby-9.3.2.0
12
+
13
+ ## 0.1.14 (21-12-2021)
14
+
15
+ Features:
16
+
17
+ - Adding unpaginated query to resource proxy with preloaded records. This will help in getting count on API.
18
+
19
+ ## 0.1.15 (21-06-2022)
20
+
21
+ Features:
22
+
23
+ - Relationships mentioned in sparse field param (and not in include), will now be returned in relationship block of response
24
+
25
+ ## 0.1.20
26
+
27
+ Features:
28
+
29
+ - With graphiti config variable "allow_sidepost" you can allow/disallow sideposting, by default it is allowed.
30
+
31
+ ## 0.1.21
32
+
33
+ Fixes:
34
+
35
+ - Runner#proxy keyword arguments
36
+
37
+ ## 0.1.22
38
+
39
+ Fixes:
40
+
41
+ - when rendering preloaded resources, we were not applying scoping. Now we are skipping around_scoping callback too.
42
+
43
+ ## 0.1.23 (29-04-2024)
44
+
45
+ Features:
46
+
47
+ - Added support for UUID
48
+
49
+ ## 0.1.24 (18-06-2024)
50
+
51
+ Features:
52
+
53
+ - Added preliminary support for Sideload backed by function instead of model association
54
+
55
+ ## 0.1.25 (04-12-2024)
56
+
57
+ Features:
58
+
59
+ - Added support to preload extra_fields for the main resource, replacing N+1 queries with a single query. This does not apply to sideloaded resources.
60
+
61
+ ## 0.2.0 (01-24-2025)
62
+
63
+ Features:
64
+
65
+ - Added MRI support
66
+ - Added support for rails 8
67
+
68
+ Breaking changes:
69
+
70
+ - Removed support for graphiti <= 1.6.3
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Graphiti ActiveGraph
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/graphiti-activegraph.svg)](https://badge.fury.io/rb/graphiti-activegraph) [![Build Status](https://github.com/mrhardikjoshi/graphiti-activegraph/actions/workflows/specs.yml/badge.svg?branch=master)](https://github.com/mrhardikjoshi/graphiti-activegraph/actions?query=branch%3Amaster) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
4
+
5
+ An adapter to make Graphiti work with the ActiveGraph(former Neo4jrb) OGM. This gem allows you to easily build [jsonapi.org](https://jsonapi.org) compatible APIs for GraphDB using [Graphiti](https://www.graphiti.dev) and [ActiveGraph](https://github.com/neo4jrb/activegraph).
6
+
7
+ ## Installation
8
+ Add this line to your application's `Gemfile`:
9
+ ```ruby
10
+ gem 'graphiti-activegraph'
11
+ ```
12
+
13
+ And then execute:
14
+ ```shell
15
+ bundle install
16
+ ```
17
+
18
+ Or install it yourself as:
19
+ ```shell
20
+ gem install graphiti-activegraph
21
+ ```
22
+
23
+ ## Usage
24
+ While defining a Resource class, inherit it from `Graphiti::ActiveGraph::Resource`
25
+ ```ruby
26
+ class PlanetResource < Graphiti::ActiveGraph::Resource
27
+ ```
28
+
29
+ For model backed by `ApplicationRelationship` instead of `ApplicationNode`, we have to set `relationship_resource` to `true` while defining resource class.
30
+ ```ruby
31
+ class RelationshipBackedResource < Graphiti::ActiveGraph::Resource
32
+ self.relationship_resource = true
33
+ end
34
+ ```
35
+
36
+ ## Documentation
37
+ ### Key Differences from Graphiti
38
+ #### Efficient Sideloading:
39
+ Unlike Graphiti, which executes multiple queries for sideloading, graphiti-activegraph leverages `with_ordered_associations` from ActiveGraph to fetch sideloaded data in a single query, improving performance.
40
+
41
+ #### Sideposting Behavior:
42
+ graphiti-activegraph allows assigning and unassigning relationships via sideposting but does not support modifying a resource’s attributes through sideposting.
43
+
44
+ #### Thread Context Handling:
45
+ Graphiti stores context using `Thread.current[]`, which does not persist across different fibers within the same thread. In graphiti-activegraph, when running on MRI (non-JRuby environments), the gem uses `thread_variable_get` and `thread_variable_set`. Ensuring the context remains consistent across different fibers in the same thread.
46
+
47
+ ### New Features in graphiti-activegraph
48
+ #### Rendering Preloaded Objects Without Extra Queries
49
+ graphiti-activegraph introduces two new methods on the Graphiti resource class:
50
+ `with_preloaded_obj(record, params)` – Renders a single preloaded ActiveGraph object without querying the database.
51
+ `all_with_preloaded(records, params)` – Renders multiple preloaded ActiveGraph objects without additional queries.
52
+ **Note:** These methods assume that the provided records are final and will not apply Graphiti’s filtering, sorting, or scoping logic.
53
+
54
+ #### Efficient Deep Sorting
55
+ Graphiti does not natively support deep sorting (i.e., sorting based on attributes of associated resources), `graphiti-activegraph` adds this feature by allowing you to chain associations using dot (.) notation.
56
+ For example, to sort Post records based on their Author's Country's name, you can use following query string:
57
+ ```
58
+ sort=author.country.name&include=author.country
59
+ ```
60
+ Here, `Author` and `Country` are associated resources, and `name` is an attribute on `Country`. The posts will be sorted by the country's name. You can chain any number of associations in this way to achieve deep sorting.
61
+ Note: The `include` parameter must be present and include the full association path (`author.country`) for the sorting to work correctly.
62
+
63
+ #### Response Payload Links control
64
+ Control the links in response payload via request query params:
65
+ - `pagination_links=true|false` — toggle top-level pagination links
66
+ - `links=true|false` — toggle links inside each resource object
67
+
68
+ ## Contributing
69
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mrhardikjoshi/graphiti-activegraph. This project is intended to be a safe, welcoming space for collaboration.
70
+
71
+ ## Release
72
+ 1. Make sure version file is updated/incremented in master branch
73
+ 2. git checkout master
74
+ 3. git pull origin master
75
+ 4. git tag v1.2.3
76
+ 5. git push origin v1.2.3
77
+ 6. gem build graphiti-activegraph.gemspec
78
+ 7. gem push graphiti-activegraph-1.2.3.gem
79
+
80
+ ## License
81
+ The gem is available as open-source under the terms of the MIT License.
@@ -19,10 +19,14 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_dependency 'graphiti', '>= 1.6.4'
21
21
  spec.add_dependency 'activegraph', '>= 12.0.0.beta.5'
22
+ spec.add_dependency 'parslet', '>= 2.0.0'
23
+ spec.add_dependency 'activegraph-extensions', '>= 0.1.0'
22
24
 
23
25
  spec.add_development_dependency 'graphiti_spec_helpers', '>= 1.0.0'
24
26
  spec.add_development_dependency 'standard'
25
27
  spec.add_development_dependency 'pry'
28
+ spec.add_development_dependency 'ffaker'
29
+ spec.add_development_dependency 'factory_bot_rails'
26
30
  spec.add_development_dependency 'rake', '>= 10.0'
27
31
  spec.add_development_dependency 'rspec', '>= 3.9.0'
28
32
  end
@@ -1,6 +1,6 @@
1
1
  module Graphiti::ActiveGraph
2
2
  module Adapters
3
- class ActiveGraph < ::Graphiti::Adapters::Abstract
3
+ class ActiveGraph < Graphiti::Adapters::Abstract
4
4
  def self.sideloading_classes
5
5
  {
6
6
  has_many: Graphiti::ActiveGraph::Adapters::ActiveGraph::HasManySideload,
@@ -0,0 +1,17 @@
1
+ module Graphiti::ActiveGraph::Extensions
2
+ require 'ostruct' if RUBY_PLATFORM != 'java'
3
+
4
+ module Context
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ def context
9
+ Thread.current.thread_variable_get(:context) || {}.tap(&method(:context=))
10
+ end
11
+
12
+ def context=(val)
13
+ Thread.current.thread_variable_set(:context, val)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ module Graphiti::ActiveGraph::Extensions::Resources
2
+ module Authorizationable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class << self
7
+ attr_reader :authorize_attributes
8
+ end
9
+ end
10
+
11
+ class_methods do
12
+ def attribute_authorization
13
+ @authorize_attributes = true
14
+ end
15
+
16
+ def authorize_attributes?
17
+ @authorize_attributes
18
+ end
19
+
20
+ def readable_attributes
21
+ attributes.select { |_attr_name, options_map| options_map[:readable] }
22
+ end
23
+
24
+ def readable_sideloads
25
+ sideloads.select { |_sideloads_name, sideload_obj| sideload_obj.readable? }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ module Graphiti::ActiveGraph::Extensions::Resources
2
+ module PayloadCombinable
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ def combine(attributes, relationships)
7
+ attributes.merge(relationship_attributes(relationships))
8
+ end
9
+
10
+ def relationship_attributes(relationships)
11
+ relationships.map { |k, v| [k, extract_ids(v)] }.to_h
12
+ end
13
+
14
+ def extract_ids(object)
15
+ object.is_a?(Array) ? object.map { |o| extract_ids(o) } : object[:attributes][:id]
16
+ end
17
+
18
+ def remove_undeclared_attributes(attr_map)
19
+ permitted_attrs = all_attributes.keys
20
+ attr_map.select! { |attr_name, _value| permitted_attrs.include?(attr_name.to_sym) }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module Graphiti::ActiveGraph::Extensions::Resources
2
+ module Preloadable
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ def with_preloaded_obj(obj, params)
7
+ id = params[:data].try(:[], :id) || params.delete(:id)
8
+ params[:filter] ||= {}
9
+ params[:filter][:id] = id if id
10
+
11
+ build(params, nil, raise_on_missing: false, preloaded: obj)
12
+ end
13
+
14
+ def all_with_preloaded(obj_arr, params)
15
+ build(params, nil, single: false, raise_on_missing: false, preloaded: obj_arr)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Graphiti::ActiveGraph::Extensions::Resources
2
+ module Rel
3
+ extend ActiveSupport::Concern
4
+
5
+ def relation_resource?
6
+ self.class.relation_resource?
7
+ end
8
+
9
+ class_methods do
10
+ def relation_resource?
11
+ config[:relation_resource] || false
12
+ end
13
+
14
+ def relationship_resource=(value)
15
+ config[:relation_resource] = value
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,66 @@
1
+ module Graphiti::ActiveGraph::JsonapiExt
2
+ class IncludeDirective < JSONAPI::IncludeDirective
3
+ attr_accessor :length, :retain_rel_limit
4
+
5
+ def initialize(include_args, options = {})
6
+ include_hash = JSONAPI::IncludeDirective::Parser.parse_include_args(include_args)
7
+ @retain_rel_limit = options.delete(:retain_rel_limit)
8
+ @hash = formulate_hash(include_hash, options)
9
+ @options = options
10
+ end
11
+
12
+ def keys
13
+ super.select(&method(:key?))
14
+ end
15
+
16
+ alias get []
17
+
18
+ def key?(key)
19
+ super && get(key).valid_length?
20
+ end
21
+
22
+ def [](key)
23
+ super&.descend(key) || {}
24
+ end
25
+
26
+ def descend(key)
27
+ length && length != '' ? dup.tap { |dup| dup.add_self_reference(key, length.to_i - 1) } : self
28
+ end
29
+
30
+ def valid_length?
31
+ length.nil? || length == '' || length.to_i.positive?
32
+ end
33
+
34
+ def to_hash
35
+ @hash.each_with_object({}) do |(key, value), hash|
36
+ key = "#{key}*#{value.length}".to_sym if value.length
37
+ hash[key] = value.to_hash unless value == self
38
+ end
39
+ end
40
+
41
+ def add_self_reference(key, length)
42
+ @hash = @hash.merge(key => self)
43
+ self.length = length
44
+ end
45
+
46
+ private
47
+
48
+ def formulate_hash(include_hash, options)
49
+ include_hash.each_with_object({}) do |(key, value), hash|
50
+ rel_name, rel_length = extract_rel_meta(key)
51
+
52
+ hash[rel_name] = self.class.new(value, options_with_retain_rel_limit(options)).tap do |directive|
53
+ directive.add_self_reference(rel_name, rel_length) if key.to_s.match?(/.+\*(\d*)\z/)
54
+ end
55
+ end
56
+ end
57
+
58
+ def extract_rel_meta(key)
59
+ Graphiti::ActiveGraph::Util::Transformers::RelationParam.new(key).split_rel_length(retain_rel_limit)
60
+ end
61
+
62
+ def options_with_retain_rel_limit(options)
63
+ options.merge(retain_rel_limit:)
64
+ end
65
+ end
66
+ end
@@ -36,12 +36,39 @@ module Graphiti::ActiveGraph
36
36
  []
37
37
  end
38
38
 
39
+ def include_directive
40
+ @include_directive ||= Graphiti::ActiveGraph::JsonapiExt::IncludeDirective.new(@include_param, retain_rel_limit: true)
41
+ end
42
+
39
43
  def parse_sort_criteria_hash(hash)
40
44
  hash.map { |key, value| [key.to_s.split('.').map(&:to_sym), value] }.to_h
41
45
  end
42
46
 
47
+ def links?
48
+ [:json, :xml, 'json', 'xml'].exclude?(params[:format]) && show_resource_links?
49
+ end
50
+
51
+ def pagination_links?
52
+ action != :find && show_pagination_links?
53
+ end
54
+
43
55
  private
44
56
 
57
+ def show_pagination_links?
58
+ return @show_pagination_links unless @show_pagination_links.nil?
59
+ @show_pagination_links = read_link_params(:pagination_links)
60
+ end
61
+
62
+ def show_resource_links?
63
+ return @show_resource_links unless @show_resource_links.nil?
64
+
65
+ @show_resource_links = read_link_params(:links)
66
+ end
67
+
68
+ def read_link_params(name)
69
+ ActiveModel::Type::Boolean.new.cast(params[name]) != false
70
+ end
71
+
45
72
  def sort_criteria(sort)
46
73
  sort.split(',').map(&method(:sort_hash)).map(&method(:parse_sort_criteria_hash))
47
74
  end
@@ -1,41 +1,52 @@
1
1
  module Graphiti
2
2
  module ActiveGraph
3
- module Resource
4
- def relation_resource?
5
- config[:relation_resource] || false
3
+ class Resource < Graphiti::Resource
4
+ include Extensions::Resources::Authorizationable
5
+ include Extensions::Resources::PayloadCombinable
6
+ include Extensions::Resources::Preloadable
7
+ include Extensions::Resources::Rel
8
+
9
+ self.adapter = Adapters::ActiveGraph
10
+ self.abstract_class = true
11
+
12
+ def self.use_uuid
13
+ define_singleton_method(:inherited) do |klass|
14
+ super(klass)
15
+ klass.attribute :id, :uuid
16
+ end
6
17
  end
7
18
 
8
- def relationship_resource=(value)
9
- config[:relation_resource] = value
19
+ def self.guard_nil_id!(params)
10
20
  end
11
21
 
12
- def with_preloaded_obj(obj, params)
13
- id = params[:data].try(:[], :id) || params.delete(:id)
14
- params[:filter] ||= {}
15
- params[:filter][:id] = id if id
22
+ def self.extra_attribute?(name)
23
+ extra_attributes.has_key?(name)
24
+ end
16
25
 
17
- build(params, nil, raise_on_missing: false, preloaded: obj)
26
+ def self.sideload_config(sideload_name)
27
+ config[:sideloads][sideload_name]
18
28
  end
19
29
 
20
- def all_with_preloaded(obj_arr, params)
21
- build(params, nil, single: false, raise_on_missing: false, preloaded: obj_arr)
30
+ def self.sideload_resource_class(sideload_name)
31
+ sideload_config(sideload_name)&.resource_class
22
32
  end
23
33
 
24
- def guard_nil_id!(params)
34
+ def self.custom_eagerload(sideload_name)
35
+ sideload_config(sideload_name)&.custom_eagerload
25
36
  end
26
37
 
27
38
  def extra_attribute?(name)
28
- extra_attributes.has_key?(name)
39
+ self.class.extra_attribute?(name)
29
40
  end
30
- end
31
41
 
32
- module ResourceInstanceMethods
33
- def relation_resource?
34
- self.class.relation_resource?
42
+ def build_scope(base, query, opts = {})
43
+ scoping_class.new(base, self, query, opts)
35
44
  end
36
45
 
37
- def extra_attribute?(name)
38
- self.class.extra_attribute?(name)
46
+ def handle_includes(scope, includes, sorts, **opts)
47
+ includes_str = JSONAPI::IncludeDirective.new(includes, retain_rel_limit: true).to_string.split(',')
48
+ options = opts.merge(max_page_size:).merge!(authorize_scope_params)
49
+ scope.with_ordered_associations(includes_str, sorts, options)
39
50
  end
40
51
 
41
52
  def sideload_name_arr(query)
@@ -68,6 +79,20 @@ module Graphiti
68
79
  raise Errors::TypecastFailed.new(self, name, value, e, type_name)
69
80
  end
70
81
  end
82
+
83
+ def authorize_scope_params
84
+ {}
85
+ end
86
+
87
+ private
88
+
89
+ def scoping_class
90
+ Scope
91
+ end
92
+
93
+ def update_foreign_key(*)
94
+ true
95
+ end
71
96
  end
72
97
  end
73
98
  end
@@ -1,5 +1,5 @@
1
1
  module Graphiti::ActiveGraph
2
- module Resource
2
+ module Resources
3
3
  module Interface
4
4
  extend ActiveSupport::Concern
5
5
  class_methods do
@@ -1,5 +1,5 @@
1
1
  module Graphiti::ActiveGraph
2
- module Resource
2
+ module Resources
3
3
  module Persistence
4
4
  def update(update_params, meta = nil)
5
5
  model_instance = nil