activegraph-extensions 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b28feac8e1598634606281d5dd8b70f019ac3038682abc635a5ef1d92c0e8b1e
4
+ data.tar.gz: 1c021a54e63267817077da6942f9fa4f0c61437e112de2677f54db7f440f653e
5
+ SHA512:
6
+ metadata.gz: e6094af1c1c28ea6af47650e7b8399703e6c356addc72ac454b5c862c92fea563e284a5b80a96435cb41c9646a5f32f711cb0e314aea7a01c838d9476fe3d64b
7
+ data.tar.gz: 7e56b8b83defdf3d9b21887dbecad56eb21927ff5c72a742b4f477bc2ad422aa24ebc135ff054a8398e18ebae6fe83642cb31f9cd526adbce3b2ddd38ab2bddc
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Change Log
2
+
3
+ ## [0.0.3] 2023-02-23
4
+
5
+ ## Fixed
6
+
7
+ - Fixed issue where there was 500 error when we are eagerloding relations with multiple optional matches and some previous match was having authorization and next one was skipping authorization for those same nodes. This would result in a collection of related nodes where some nodes were null and corresponding next optional match nodes on path had values.
8
+
9
+ ## [0.1.0] 2025-01-13
10
+
11
+ ## Added
12
+
13
+ - Support for MRI
data/Gemfile ADDED
@@ -0,0 +1,24 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'listen', '< 3.1'
6
+ active_model_version = ENV['ACTIVE_MODEL_VERSION']
7
+ gem 'activemodel', "~> #{active_model_version}" if active_model_version&.length&.positive?
8
+
9
+ #gem 'zeitwerk'
10
+
11
+ group :test do
12
+ gem 'coveralls', require: false
13
+ gem 'overcommit'
14
+ gem 'codecov', require: false
15
+ gem 'simplecov', require: false
16
+ gem 'simplecov-html', require: false
17
+ gem 'its'
18
+ gem 'test-unit'
19
+ gem 'colored'
20
+ gem 'dotenv'
21
+ gem 'timecop'
22
+ gem 'pry'
23
+ gem 'activegraph'
24
+ end
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # Extensions to activegraph gem.
@@ -0,0 +1,42 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'active_graph_extensions/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'activegraph-extensions'
8
+ s.version = ActiveGraphExtensions::VERSION
9
+
10
+ s.required_ruby_version = '>= 2.5'
11
+
12
+ s.authors = 'Amit Suryavanshi'
13
+ s.email = 'amitbsuryavabshi@mail.com'
14
+ s.homepage = 'https://github.com/neo4jrb/activegraph-extensions/'
15
+ s.summary = 'Additional features to activegraph'
16
+ s.license = 'MIT'
17
+ s.description = <<-DESCRIPTION
18
+ Additional features to activegraph, like sideload limiting, authorizing sideloads etc.
19
+ DESCRIPTION
20
+
21
+ s.require_path = 'lib'
22
+ s.files = Dir.glob('{bin,lib,config}/**/*') + %w(README.md CHANGELOG.md Gemfile activegraph-extensions.gemspec)
23
+ s.executables = []
24
+ s.extra_rdoc_files = %w( README.md )
25
+ s.rdoc_options = ['--quiet', '--title', 'Neo4j.rb', '--line-numbers', '--main', 'README.rdoc', '--inline-source']
26
+
27
+ s.add_dependency('parslet')
28
+ s.add_dependency('activegraph', '>= 12.0.0.beta.5')
29
+
30
+ s.add_development_dependency('guard')
31
+ s.add_development_dependency('guard-rspec')
32
+ s.add_development_dependency('guard-rubocop')
33
+ s.add_development_dependency('neo4j-rake_tasks', '>= 0.3.0')
34
+ s.add_development_dependency('os')
35
+ s.add_development_dependency('pry')
36
+ s.add_development_dependency('railties', '>= 4.0')
37
+ s.add_development_dependency('rake')
38
+ s.add_development_dependency('rubocop', '>= 0.56.0')
39
+ s.add_development_dependency('yard')
40
+ s.add_development_dependency('dryspec')
41
+ s.add_development_dependency('rspec', '< 3.10') # Cannot proxy frozen objects
42
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module Node
5
+ module Query
6
+ # We need earlier proxy to generate cypher in query proxy eagerloading
7
+ module QueryProxy
8
+ def branch(&block)
9
+ proxy = super
10
+ proxy.instance_variable_set(:@break_proxy, as(identity).instance_eval(&block))
11
+ proxy
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module Node
5
+ module Query
6
+ module QueryProxyEagerLoading
7
+ # Used for eager loading associations with scope
8
+ module AssociationEagerLoad
9
+ extend ActiveSupport::Concern
10
+
11
+ class_methods do
12
+ def associations_to_eagerload
13
+ @associations_to_eagerload
14
+ end
15
+
16
+ def association_nodes(key, ids, filter)
17
+ send(@associations_to_eagerload[key], ids, filter)
18
+ end
19
+
20
+ def eagerload_associations(config)
21
+ @associations_to_eagerload = config
22
+ end
23
+
24
+ def eagerload_association?(key)
25
+ @associations_to_eagerload.keys.include?(key)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module Node
5
+ module Query
6
+ module QueryProxyEagerLoading
7
+ module AssociationLimiting
8
+ def self.included(base)
9
+ base.attr_reader(:max_page_size)
10
+ base.attr_reader(:paginate)
11
+ end
12
+
13
+ private
14
+
15
+ def rel_collection_str(path)
16
+ collection_name = "[#{relationship_name(path)}, #{escape(path_name(path))}] "
17
+ collection = apply_limit?(path) ? "apoc.agg.slice(#{collection_name}, 0, #{association_limit(path)})" : "collect(#{collection_name})"
18
+ "#{collection} AS #{escape("#{path_name(path)}_collection")}"
19
+ end
20
+
21
+ def apply_limit?(path)
22
+ !multipath?(path) && (path.last&.association_limit || paginate)
23
+ end
24
+
25
+ def relationship_name(path)
26
+ if path.last.rel_length
27
+ "last(relationships(#{escape("#{path_name(path)}_path")}))"
28
+ else
29
+ escape("#{path_name(path)}_rel")
30
+ end
31
+ end
32
+
33
+ def convert_to_list(collection_name, limit)
34
+ limit.present? ? "apoc.agg.slice(#{collection_name}, 0, #{limit})" : "collect(#{collection_name})"
35
+ end
36
+
37
+ def association_limit(path)
38
+ limit = path.last&.association_limit
39
+ limit.blank? || limit.to_i > max_page_size ? max_page_size : limit
40
+ end
41
+
42
+ def with_association_query_part(base_query, path, previous_with_vars)
43
+ with_args = [identity, rel_collection_str(path), *previous_with_vars]
44
+
45
+ optional_match_with_where(base_query, path, previous_with_vars).with(with_args)
46
+ end
47
+
48
+ def limit_node_in_where_clause(query, path)
49
+ (path.length - 1).times.inject(query) do |query_with_where, index|
50
+ query_with_where.where("`#{path_name(path[0..index])}` in [i IN #{node_from_collection(path[0..index])} | i[1]]")
51
+ end
52
+ end
53
+
54
+ def node_from_collection(path_step)
55
+ "`#{path_name(path_step)}_collection`"
56
+ end
57
+
58
+ def path_alias(node_alias)
59
+ var_fix(node_alias, :path)
60
+ end
61
+
62
+ def rel_alias(node_alias)
63
+ var_fix(node_alias, :rel)
64
+ end
65
+
66
+ def multipath?(path)
67
+ path.size > 1
68
+ end
69
+
70
+ def association_limit_present?(path)
71
+ association_limit(path).present?
72
+ end
73
+
74
+ def multipath_with_sideload_limit?(path)
75
+ multipath?(path) && association_limit_present?(path[0..0])
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module Node
5
+ module Query
6
+ module QueryProxyEagerLoading
7
+ # Used to append auth scopes to query proxy eagerloading
8
+ module EagerLoadingOrder
9
+ def optional_order(query, path, previous_with_vars)
10
+ node_alias = path_name(path)
11
+ order_clause = order_clause_for_query(node_alias)
12
+ if path.last.rel_length
13
+ order_clause.reject! { |el| el.include?('_rel') }
14
+ query.order("length(`#{node_alias}_path`)", *order_clause)
15
+ .with(*with_variables(path, node_alias, previous_with_vars))
16
+ else
17
+ query.order(*order_clause).with(*with_variables(path, node_alias, previous_with_vars))
18
+ end
19
+ end
20
+
21
+ def order_clause_for_query(node_alias)
22
+ (order = @order_spec&.fetch(node_alias, nil)) ? order.map(&method(:nested_order_clause).curry.call(node_alias)) : []
23
+ end
24
+
25
+ def nested_order_clause(node_alias, order_spec)
26
+ [node_or_rel_alias(node_alias, order_spec), name(order_spec)].join('.')
27
+ end
28
+
29
+ def order_clause(key, order_spec)
30
+ property_with_direction = name(order_spec)
31
+ node_alias = node_aliase_for_collection(key, order_spec) || node_aliase_for_order(property_with_direction)
32
+ [node_alias, property_with_direction].compact.join('.')
33
+ end
34
+
35
+ def skip_order?
36
+ @order_spec.blank? || @order_spec.keys.all?(&:blank?)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module Node
5
+ module Query
6
+ module QueryProxyEagerLoading
7
+ # Tree allowing storage of additional information about the associations
8
+ class EnhancedTree < ::ActiveGraph::Node::Query::QueryProxyEagerLoading::AssociationTree
9
+ attr_reader :options, :association_limit
10
+
11
+ def initialize(model, name = nil, rel_length = nil, association_limit = nil)
12
+ @association_limit = association_limit
13
+ super(model, name, rel_length)
14
+ end
15
+
16
+ def add_spec_and_validate(spec)
17
+ add_spec(spec)
18
+ validate_for_zero_length_paths
19
+ end
20
+
21
+ def validate_for_zero_length_paths
22
+ fail 'Can not eager load more than one zero length path.' if values.count(&:zero_length_path?) > 1
23
+ end
24
+
25
+ def zero_length_path?
26
+ rel_length&.fetch(:min, nil)&.to_s == '0' || values.any?(&:zero_length_path?)
27
+ end
28
+
29
+ def add_key(key, length = nil, assoc_limit = nil)
30
+ self[key] ||= self.class.new(model, key, length, assoc_limit)
31
+ end
32
+
33
+ def add_nested(key, value, length = nil, assoc_limit = nil)
34
+ add_key(key, length, assoc_limit).add_spec(value)
35
+ end
36
+
37
+ def process_string(str)
38
+ map = StringParsers::RelationParser.new.parse(str)
39
+ add_nested(map[:rel_name].to_sym, map[:rest_str].to_s.presence, map[:length_part], map[:limit_digit])
40
+ end
41
+
42
+ def process_hash(spec)
43
+ spec = spec.dup
44
+ @options = spec.delete(:_options)
45
+ super(spec)
46
+ end
47
+
48
+ def target_class(model, key)
49
+ association = model.associations[key.to_sym]
50
+ fail "Invalid association: #{[*path, key].join('.')}" unless association
51
+ model.associations[key].target_class
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module Node
5
+ module Query
6
+ module QueryProxyEagerLoading
7
+ # Used to append auth scopes to query proxy eagerloading
8
+ module ScopeEagerLoading
9
+ def authorized_rel(path, var)
10
+ rel_model = relationship_model(path)
11
+ return {} if @opts.blank? || !(auth_scope = authorized_scope(rel_model, path))
12
+ conf = { rels: [], chain: {} }
13
+ proxy = auth_scope.call(var, "#{var}_rel", @opts.merge(properties: properties_for(rel_model),
14
+ rel_length: path.last.rel_length))
15
+ proxy_rel_parts(proxy.instance_variable_get(:@break_proxy) || proxy, conf)
16
+ conf
17
+ end
18
+
19
+ def properties_for(rel_model)
20
+ return [] unless @opts[:properties]
21
+ @opts[:properties].select { |prop| prop.model.name == rel_model.name }.map(&:name)
22
+ end
23
+
24
+ def relationship_model(path)
25
+ path[0..-2].inject(model) { |mod, rel| mod.send(rel.name).model }
26
+ end
27
+
28
+ def authorized_scope(rel_model, path)
29
+ rel_model.scopes["authorized_#{path.last.association.name}".to_sym]
30
+ end
31
+
32
+ def proxy_rel_parts(auth_proxy, conf)
33
+ return unless auth_proxy&.association
34
+ rel_length = auth_proxy.instance_variable_get(:@rel_length)
35
+ conf[:rels] << relationship_part(auth_proxy.association, auth_proxy.identity, rel_length)
36
+ assign_config_chain(conf, auth_proxy, rel_length)
37
+ proxy_rel_parts(auth_proxy.query_proxy, conf)
38
+ end
39
+
40
+ def assign_config_chain(conf, auth_proxy, rel_length)
41
+ return unless (auth_chain = auth_proxy.instance_variable_get(:@chain))
42
+ conf[:chain][[auth_proxy.identity, rel_length ? "#{auth_proxy.identity}_rel" : nil]] = auth_chain
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,149 @@
1
+ module ActiveGraphExtensions
2
+ module Node
3
+ module Query
4
+ module QueryProxyEagerLoading
5
+ extend ActiveSupport::Concern
6
+ include AssociationLimiting
7
+ include ScopeEagerLoading
8
+ include EagerLoadingOrder
9
+
10
+ def association_tree_class
11
+ EnhancedTree
12
+ end
13
+
14
+ def with_ordered_associations(spec, order, opts = {})
15
+ @max_page_size = opts[:max_page_size]
16
+ @paginate = opts[:paginate]
17
+ @with_vars = opts[:with_vars]
18
+ @order_spec = order.with_indifferent_access unless spec.empty?
19
+ @opts = opts
20
+ with_associations(spec)
21
+ end
22
+
23
+ def first
24
+ limit(1).to_a.first
25
+ end
26
+
27
+ class_methods do
28
+ def rel?(order_spec)
29
+ order_spec.is_a?(Hash) ? 0 : 1
30
+ end
31
+ end
32
+
33
+ def with_associations(*spec)
34
+ new_link.tap do |new_query_proxy|
35
+ new_query_proxy.with_associations_tree = with_associations_tree.clone
36
+ new_query_proxy.with_associations_tree.add_spec_and_validate(spec)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def query_from_association_tree
43
+ previous_with_vars = defalut_previous_with_vars
44
+ with_associations_tree.paths.inject(query_as(identity).with(base_query_with_vars)) do |query, path|
45
+ with_association_query_part(query, path, previous_with_vars).tap do
46
+ previous_with_vars << var_fix(path_name(path), :collection)
47
+ end
48
+ end
49
+ end
50
+
51
+ def defalut_previous_with_vars
52
+ @with_vars&.dup || []
53
+ end
54
+
55
+ def base_query_with_vars
56
+ [ensure_distinct(identity)] + (@with_vars || [])
57
+ end
58
+
59
+ def optional_match_with_where(query, path, vars)
60
+ computed_query = super
61
+ computed_query = limit_node_in_where_clause(computed_query, path) if multipath?(path)
62
+ skip_order? && !path.last.rel_length ? computed_query : optional_order(computed_query, path, vars)
63
+ end
64
+
65
+ def optional_match(base_query, path)
66
+ start_path = "#{escape("#{path_name(path)}_path")}=(#{identity})"
67
+ conf = authorized_rel(path, path_name(path[0..-1]))
68
+ query = construct_optional_match(start_path, base_query, conf[:rels] ? path[0..-2] : path, conf[:rels])
69
+ conf[:rels] ? apply_chain(conf[:chain], query) : query
70
+ end
71
+
72
+ def construct_optional_match(start_path, base_query, path, scope_rels)
73
+ base_query.optional_match(
74
+ "#{start_path}#{path.each_with_index.map do |element, index|
75
+ relationship_part(element.association, path_name(path[0..index]), element.rel_length)
76
+ end.join}#{(scope_rels || []).reverse.join}"
77
+ )
78
+ end
79
+
80
+ def apply_chain(chain, query)
81
+ chain.each do |key, links|
82
+ query = links.inject(query) do |q, link|
83
+ args = link.args(*key)
84
+ args.is_a?(Array) ? q.send(link.clause, *args) : q.send(link.clause, args)
85
+ end
86
+ end
87
+ query
88
+ end
89
+
90
+ def with_variables(path, node_alias, previous_with_vars)
91
+ [identity, path.last.rel_length ? path_alias(node_alias) : rel_alias(node_alias), var_fix(node_alias)] +
92
+ previous_with_vars
93
+ end
94
+
95
+ def before_pluck(query)
96
+ return query if skip_order? && !include_with_path_length?
97
+ base_query = query.order(
98
+ (@order_spec || []).flat_map { |key, order_specs| order_specs.map(&method(:order_clause).curry.call(key)) }
99
+ )
100
+ query_from_chain(@postponed_chain, base_query, identity)
101
+ end
102
+
103
+ def node_aliase_for_collection(key, order_spec)
104
+ "#{var(key, :collection, &:itself)}[0][#{self.class.rel?(order_spec)}]" if key.present?
105
+ end
106
+
107
+ def node_aliase_for_order(property_with_direction)
108
+ identity unless @with_vars&.include?(property_with_direction.split(' ').first.to_sym)
109
+ end
110
+
111
+ def name(order_spec)
112
+ Array(order_spec).flatten.last.to_s
113
+ end
114
+
115
+ def node_or_rel_alias(node_alias, order_spec)
116
+ var(node_alias, order_spec.is_a?(Hash) ? :rel : nil, &:itself)
117
+ end
118
+
119
+ CLAUSES_TO_POSTPONE = %i[limit order skip].freeze
120
+
121
+ def include_with_path_length?(path = @with_associations_tree)
122
+ path.present? && (path.rel_length.present? || path.any? { |_, val| include_with_path_length?(val) })
123
+ end
124
+
125
+ def chain
126
+ return super if skip_order? && !include_with_path_length?
127
+ clauses = !skip_order? ? CLAUSES_TO_POSTPONE : %i[order]
128
+ @postponed_chain, other_chain = super.partition { |link| clauses.include?(link.clause) }
129
+ other_chain
130
+ end
131
+
132
+ def perform_query
133
+ @_cache = ActiveGraph::Node::Query::QueryProxyEagerLoading::IdentityMap.new
134
+ build_query
135
+ .map do |record, eager_data|
136
+ record = cache_and_init(record, with_associations_tree)
137
+ eager_data.zip(with_associations_tree.paths.map(&:last)).each do |eager_records, element|
138
+ eager_records.each do |eager_record|
139
+ next unless eager_record.first&.type&.to_s == element.association.relationship_type.to_s
140
+ add_to_cache(*eager_record, element)
141
+ end
142
+ end
143
+ record
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module StringParsers
5
+ # Parsing relationships with length
6
+ class RelationParser < ::Parslet::Parser
7
+ include SharedRules
8
+
9
+ # TODO: It is very bad to build a grammar with none terminals. Please note that none here are necessary to
10
+ # mimic the previous behavior of `repeat` which is `repeat(0)` which effectively allows empty strings as
11
+ # identifiers
12
+ rule(:zero) { str('0') }
13
+ rule(:length_1) { zero.as(:min) >> range >> number?.maybe.as(:max) }
14
+ rule(:length_2) { number?.maybe.as(:max) }
15
+ rule(:length) { asterisk >> (length_1 | length_2) }
16
+ rule(:limit) { number?.as(:limit_digit) >> asterisk }
17
+ rule(:key) { limit.maybe >> rel >> length.as(:length_part).maybe }
18
+ rule(:anything) { match('.').repeat }
19
+ rule(:root) { key >> dot.maybe >> anything.maybe.as(:rest_str) }
20
+ rule(:rel_sequence) { infix_expression(key, [dot, 1, :left]) }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphExtensions
4
+ module StringParsers
5
+ module SharedRules
6
+ extend ActiveSupport::Concern
7
+ VAR_CHAR = 'a-z_'
8
+ included do
9
+ rule(:asterisk) { str('*') }
10
+ rule(:number) { match('[\d]').repeat(1) }
11
+ rule(:none) { str('') }
12
+ rule(:number?) { number | none }
13
+ rule(:range) { str('..') }
14
+ rule(:dot) { str('.') }
15
+ rule(:identifier) { match("[#{VAR_CHAR}]") >> match("[#{VAR_CHAR}0-9]").repeat(0) }
16
+ rule(:identifier?) { identifier | none }
17
+ rule(:rel) { identifier?.as(:rel_name) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveGraphExtensions
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,12 @@
1
+ require 'active_graph'
2
+ require 'parslet'
3
+
4
+ module ActiveGraphExtensions
5
+ end
6
+
7
+ Zeitwerk::Loader.for_gem.setup
8
+
9
+ ActiveGraph::Node::Query::QueryProxy.include ActiveGraphExtensions::Node::Query::QueryProxyEagerLoading
10
+ ActiveGraph::Node::Query::QueryProxy.prepend ActiveGraphExtensions::Node::Query::QueryProxy
11
+
12
+ ActiveGraph::Node.include ActiveGraphExtensions::Node::Query::QueryProxyEagerLoading::AssociationEagerLoad
metadata ADDED
@@ -0,0 +1,259 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activegraph-extensions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Amit Suryavanshi
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-01-13 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: parslet
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: activegraph
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 12.0.0.beta.5
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 12.0.0.beta.5
40
+ - !ruby/object:Gem::Dependency
41
+ name: guard
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: guard-rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: guard-rubocop
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: neo4j-rake_tasks
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 0.3.0
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: 0.3.0
96
+ - !ruby/object:Gem::Dependency
97
+ name: os
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: railties
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '4.0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '4.0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: rake
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: rubocop
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: 0.56.0
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: 0.56.0
166
+ - !ruby/object:Gem::Dependency
167
+ name: yard
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: dryspec
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: rspec
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "<"
199
+ - !ruby/object:Gem::Version
200
+ version: '3.10'
201
+ type: :development
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "<"
206
+ - !ruby/object:Gem::Version
207
+ version: '3.10'
208
+ description: " Additional features to activegraph, like sideload limiting, authorizing
209
+ sideloads etc.\n"
210
+ email: amitbsuryavabshi@mail.com
211
+ executables: []
212
+ extensions: []
213
+ extra_rdoc_files:
214
+ - README.md
215
+ files:
216
+ - CHANGELOG.md
217
+ - Gemfile
218
+ - README.md
219
+ - activegraph-extensions.gemspec
220
+ - lib/active_graph_extensions.rb
221
+ - lib/active_graph_extensions/node/query/query_proxy.rb
222
+ - lib/active_graph_extensions/node/query/query_proxy_eager_loading.rb
223
+ - lib/active_graph_extensions/node/query/query_proxy_eager_loading/association_eager_load.rb
224
+ - lib/active_graph_extensions/node/query/query_proxy_eager_loading/association_limiting.rb
225
+ - lib/active_graph_extensions/node/query/query_proxy_eager_loading/eager_loading_order.rb
226
+ - lib/active_graph_extensions/node/query/query_proxy_eager_loading/enhanced_tree.rb
227
+ - lib/active_graph_extensions/node/query/query_proxy_eager_loading/scope_eager_loading.rb
228
+ - lib/active_graph_extensions/string_parsers/relation_parser.rb
229
+ - lib/active_graph_extensions/string_parsers/shared_rules.rb
230
+ - lib/active_graph_extensions/version.rb
231
+ homepage: https://github.com/neo4jrb/activegraph-extensions/
232
+ licenses:
233
+ - MIT
234
+ metadata: {}
235
+ rdoc_options:
236
+ - "--quiet"
237
+ - "--title"
238
+ - Neo4j.rb
239
+ - "--line-numbers"
240
+ - "--main"
241
+ - README.rdoc
242
+ - "--inline-source"
243
+ require_paths:
244
+ - lib
245
+ required_ruby_version: !ruby/object:Gem::Requirement
246
+ requirements:
247
+ - - ">="
248
+ - !ruby/object:Gem::Version
249
+ version: '2.5'
250
+ required_rubygems_version: !ruby/object:Gem::Requirement
251
+ requirements:
252
+ - - ">="
253
+ - !ruby/object:Gem::Version
254
+ version: '0'
255
+ requirements: []
256
+ rubygems_version: 3.6.2
257
+ specification_version: 4
258
+ summary: Additional features to activegraph
259
+ test_files: []