advanced_relationship_management 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/advanced_relationship_management/advanced_scoping.rb +25 -0
- data/lib/advanced_relationship_management/configuration.rb +16 -0
- data/lib/advanced_relationship_management/counts.rb +14 -0
- data/lib/advanced_relationship_management/cycle_detection.rb +16 -0
- data/lib/advanced_relationship_management/filter_scope.rb +12 -0
- data/lib/advanced_relationship_management/lazy_loading.rb +19 -0
- data/lib/advanced_relationship_management/path_to_root.rb +33 -0
- data/lib/advanced_relationship_management/railtie.rb +7 -0
- data/lib/advanced_relationship_management/recursive_relationships.rb +98 -0
- data/lib/advanced_relationship_management/relationship_visualizations.rb +11 -0
- data/lib/advanced_relationship_management/sibling_relationships.rb +12 -0
- data/lib/advanced_relationship_management/version.rb +5 -0
- data/lib/advanced_relationship_management.rb +88 -0
- data/lib/generators/advanced_relationship_management/install/install_generator.rb +23 -0
- data/lib/generators/advanced_relationship_management/install/templates/create_graph_edges_migration.rb +10 -0
- data/lib/tasks/advanced_relationship_management_tasks.rake +7 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8f221df65f0856c3c8b946e80ecda01dd2a32557497ea66d9e3bee45e9fe7b24
|
4
|
+
data.tar.gz: e32baec528fab08a83c1f84cec9b844e65fc76e7ee7bea63d5e5100f778edc7e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 513d6327b555cde3e42101a92ddf79e8fd0b7fa00edc3e190c042733b59ac6c2d9257c76b690b430fe2b39cd672a302fc4e8662bfb00041d34f87464823ebda3
|
7
|
+
data.tar.gz: c9a7f3608959234358bde548208b5541cb2d8f82aac5c8e5650131e0a6743473073a76cb22d89c61aa52f97ffd22b5e74802ead2995837e575a5d6b10fbb62bb
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module AdvancedScoping
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
scope :roots, -> { where(parent_id: nil) }
|
7
|
+
scope :by_depth, ->(depth) { where("depth <= ?", depth) }
|
8
|
+
|
9
|
+
def depth
|
10
|
+
if AdvancedRelationshipManagement.enable_caching
|
11
|
+
Rails.cache.fetch([self, "depth"]) { calculate_depth }
|
12
|
+
else
|
13
|
+
calculate_depth
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def calculate_depth
|
18
|
+
ancestors.size
|
19
|
+
end
|
20
|
+
|
21
|
+
scope :with_min_descendants, ->(min) { select { |record| record.descendants.size >= min } }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module Configuration
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
mattr_accessor :default_parent_column, :default_child_column, :enable_caching
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def configure
|
11
|
+
yield self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module CycleDetection
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
validate :no_cycles
|
7
|
+
end
|
8
|
+
|
9
|
+
def no_cycles
|
10
|
+
if ancestors.include?(self)
|
11
|
+
errors.add(:base, "Cycle detected in the hierarchy")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module LazyLoading
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
scope :lazy_load_children, -> { includes(:children) }
|
7
|
+
scope :lazy_load_parent, -> { includes(:parent) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def lazy_descendants
|
11
|
+
self.class.lazy_load_children.where("#{self.class.parent_column_name} = ?", self.id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def lazy_ancestors
|
15
|
+
self.class.lazy_load_parent.where("#{self.class.child_column_name} = ?", self.id)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module PathToRoot
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def path_to_root(format: :array, attribute: :id)
|
6
|
+
path = []
|
7
|
+
current_node = self
|
8
|
+
while current_node
|
9
|
+
path << current_node
|
10
|
+
current_node = current_node.parent
|
11
|
+
end
|
12
|
+
path.reverse!
|
13
|
+
|
14
|
+
case format
|
15
|
+
when :array
|
16
|
+
path
|
17
|
+
when :symbolic
|
18
|
+
path.map { |node| node.public_send(attribute) }.join(' -> ')
|
19
|
+
when :json
|
20
|
+
path.map { |node| { id: node.id, attribute => node.public_send(attribute) } }.to_json
|
21
|
+
when :html
|
22
|
+
path.map { |node| "<a href='/users/#{node.id}'>#{node.public_send(attribute)}</a>" }.join(' > ')
|
23
|
+
when :reverse_symbolic
|
24
|
+
path.map { |node| node.public_send(attribute) }.reverse.join(' -> ')
|
25
|
+
when :nested_hash
|
26
|
+
path.inject(nil) { |acc, node| { id: node.id, attribute => node.public_send(attribute), parent: acc } }
|
27
|
+
else
|
28
|
+
raise ArgumentError, "Unsupported format: #{format}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module AdvancedRelationshipManagement
|
2
|
+
module RecursiveRelationships
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
after_initialize :setup_recursive_relationships
|
7
|
+
after_create :clear_cache
|
8
|
+
after_update :clear_cache
|
9
|
+
after_destroy :clear_cache
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup_recursive_relationships
|
13
|
+
return if self.class.reflect_on_association(:children) && self.class.reflect_on_association(:parent)
|
14
|
+
|
15
|
+
parent_column = self.class.parent_column_name
|
16
|
+
self.class.setup_relationships(parent_column)
|
17
|
+
end
|
18
|
+
|
19
|
+
def descendants(*scopes)
|
20
|
+
if AdvancedRelationshipManagement.enable_caching
|
21
|
+
Rails.cache.fetch([self, "descendants"] + scopes) { fetch_descendants(*scopes) }
|
22
|
+
else
|
23
|
+
fetch_descendants(*scopes)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_descendants(*scopes)
|
28
|
+
relation = self.class.find_by_sql([<<-SQL, id])
|
29
|
+
WITH RECURSIVE descendants_cte AS (
|
30
|
+
SELECT *
|
31
|
+
FROM #{self.class.table_name}
|
32
|
+
WHERE #{self.class.child_column_name} = ?
|
33
|
+
UNION ALL
|
34
|
+
SELECT #{self.class.table_name}.*
|
35
|
+
FROM #{self.class.table_name}
|
36
|
+
INNER JOIN descendants_cte ON descendants_cte.#{self.class.child_column_name} = #{self.class.table_name}.#{self.class.parent_column_name}
|
37
|
+
)
|
38
|
+
SELECT *
|
39
|
+
FROM descendants_cte
|
40
|
+
SQL
|
41
|
+
|
42
|
+
scopes.each do |scope|
|
43
|
+
relation = relation.public_send(scope) if scope.is_a?(Symbol)
|
44
|
+
relation = relation.merge(scope) if scope.is_a?(ActiveRecord::Relation)
|
45
|
+
end
|
46
|
+
|
47
|
+
relation
|
48
|
+
end
|
49
|
+
|
50
|
+
def ancestors
|
51
|
+
if AdvancedRelationshipManagement.enable_caching
|
52
|
+
Rails.cache.fetch([self, "ancestors"]) { fetch_ancestors }
|
53
|
+
else
|
54
|
+
fetch_ancestors
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def fetch_ancestors
|
59
|
+
all_ancestors = []
|
60
|
+
ActiveRecord::Base.silence do
|
61
|
+
current_node = self
|
62
|
+
while current_node.parent
|
63
|
+
all_ancestors << current_node.parent
|
64
|
+
current_node = current_node.parent
|
65
|
+
end
|
66
|
+
end
|
67
|
+
all_ancestors
|
68
|
+
end
|
69
|
+
|
70
|
+
def depth_of_descendants
|
71
|
+
descendants_with_depth = {}
|
72
|
+
descendants.each do |descendant|
|
73
|
+
descendants_with_depth[descendant] = depth(descendant)
|
74
|
+
end
|
75
|
+
descendants_with_depth
|
76
|
+
end
|
77
|
+
|
78
|
+
def depth_of_ancestors
|
79
|
+
ancestors_with_depth = {}
|
80
|
+
ancestors.each do |ancestor|
|
81
|
+
ancestors_with_depth[ancestor] = depth(ancestor)
|
82
|
+
end
|
83
|
+
ancestors_with_depth
|
84
|
+
end
|
85
|
+
|
86
|
+
def depth(node)
|
87
|
+
node.ancestors.size
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def clear_cache
|
93
|
+
Rails.cache.delete([self, "descendants"])
|
94
|
+
Rails.cache.delete([self, "ancestors"])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_record'
|
3
|
+
require_relative 'advanced_relationship_management/recursive_relationships'
|
4
|
+
require_relative 'advanced_relationship_management/relationship_visualizations'
|
5
|
+
require_relative 'advanced_relationship_management/advanced_scoping'
|
6
|
+
require_relative 'advanced_relationship_management/cycle_detection'
|
7
|
+
require_relative 'advanced_relationship_management/sibling_relationships'
|
8
|
+
require_relative 'advanced_relationship_management/counts'
|
9
|
+
require_relative 'advanced_relationship_management/path_to_root'
|
10
|
+
require_relative 'advanced_relationship_management/filter_scope'
|
11
|
+
require_relative 'advanced_relationship_management/lazy_loading'
|
12
|
+
|
13
|
+
module AdvancedRelationshipManagement
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
mattr_accessor :default_parent_column, :default_child_column, :enable_caching
|
17
|
+
|
18
|
+
self.default_parent_column = :parent_id
|
19
|
+
self.default_child_column = :id
|
20
|
+
self.enable_caching = true
|
21
|
+
|
22
|
+
included do
|
23
|
+
include AdvancedRelationshipManagement::RecursiveRelationships
|
24
|
+
include AdvancedRelationshipManagement::RelationshipVisualizations
|
25
|
+
include AdvancedRelationshipManagement::AdvancedScoping
|
26
|
+
include AdvancedRelationshipManagement::CycleDetection
|
27
|
+
include AdvancedRelationshipManagement::SiblingRelationships
|
28
|
+
include AdvancedRelationshipManagement::Counts
|
29
|
+
include AdvancedRelationshipManagement::PathToRoot
|
30
|
+
include AdvancedRelationshipManagement::FilterScope
|
31
|
+
include AdvancedRelationshipManagement::LazyLoading
|
32
|
+
end
|
33
|
+
|
34
|
+
class_methods do
|
35
|
+
def setup_relationships(parent_column = nil, child_column = nil)
|
36
|
+
parent_column ||= AdvancedRelationshipManagement.default_parent_column
|
37
|
+
child_column ||= AdvancedRelationshipManagement.default_child_column
|
38
|
+
|
39
|
+
has_many :children, class_name: name, foreign_key: parent_column, inverse_of: :parent
|
40
|
+
belongs_to :parent, class_name: name, foreign_key: parent_column, optional: true
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure_relationships(parent_column: :parent_id, child_column: :id, enable_caching: true)
|
44
|
+
@parent_column = parent_column
|
45
|
+
@child_column = child_column
|
46
|
+
AdvancedRelationshipManagement.enable_caching = enable_caching
|
47
|
+
setup_relationships(parent_column, child_column)
|
48
|
+
end
|
49
|
+
|
50
|
+
def parent_column(column_name)
|
51
|
+
@parent_column = column_name
|
52
|
+
setup_relationships(column_name, @child_column)
|
53
|
+
end
|
54
|
+
|
55
|
+
def child_column(column_name)
|
56
|
+
@child_column = column_name
|
57
|
+
setup_relationships(@parent_column, column_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def enable_caching?
|
61
|
+
AdvancedRelationshipManagement.enable_caching
|
62
|
+
end
|
63
|
+
|
64
|
+
def enable_caching(enable)
|
65
|
+
AdvancedRelationshipManagement.enable_caching = enable
|
66
|
+
end
|
67
|
+
|
68
|
+
def parent_column_name
|
69
|
+
@parent_column || AdvancedRelationshipManagement.default_parent_column
|
70
|
+
end
|
71
|
+
|
72
|
+
def child_column_name
|
73
|
+
@child_column || AdvancedRelationshipManagement.default_child_column
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module ActiveRecord
|
79
|
+
class Base
|
80
|
+
def self.silence
|
81
|
+
old_logger = ActiveRecord::Base.logger
|
82
|
+
ActiveRecord::Base.logger = nil
|
83
|
+
yield
|
84
|
+
ensure
|
85
|
+
ActiveRecord::Base.logger = old_logger
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module AdvancedRelationshipManagement
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
9
|
+
|
10
|
+
def copy_migration
|
11
|
+
migration_template "create_graph_edges_migration.rb", "db/migrate/create_graph_edges.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.next_migration_number(dirname)
|
15
|
+
if ActiveRecord::Base.timestamped_migrations
|
16
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
17
|
+
else
|
18
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: advanced_relationship_management
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jana
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-05-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rgl
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.5
|
41
|
+
description: A gem to manage complex relationships, including recursive and graph-based
|
42
|
+
relationships, for ActiveRecord models in Rails.
|
43
|
+
email:
|
44
|
+
- shanmugamjanarthan24@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- lib/advanced_relationship_management.rb
|
50
|
+
- lib/advanced_relationship_management/advanced_scoping.rb
|
51
|
+
- lib/advanced_relationship_management/configuration.rb
|
52
|
+
- lib/advanced_relationship_management/counts.rb
|
53
|
+
- lib/advanced_relationship_management/cycle_detection.rb
|
54
|
+
- lib/advanced_relationship_management/filter_scope.rb
|
55
|
+
- lib/advanced_relationship_management/lazy_loading.rb
|
56
|
+
- lib/advanced_relationship_management/path_to_root.rb
|
57
|
+
- lib/advanced_relationship_management/railtie.rb
|
58
|
+
- lib/advanced_relationship_management/recursive_relationships.rb
|
59
|
+
- lib/advanced_relationship_management/relationship_visualizations.rb
|
60
|
+
- lib/advanced_relationship_management/sibling_relationships.rb
|
61
|
+
- lib/advanced_relationship_management/version.rb
|
62
|
+
- lib/generators/advanced_relationship_management/install/install_generator.rb
|
63
|
+
- lib/generators/advanced_relationship_management/install/templates/create_graph_edges_migration.rb
|
64
|
+
- lib/tasks/advanced_relationship_management_tasks.rake
|
65
|
+
homepage: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata:
|
69
|
+
allowed_push_host: https://rubygems.org
|
70
|
+
homepage_uri: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
71
|
+
source_code_uri: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
72
|
+
changelog_uri: https://github.com/janarthanan-shanmugam/advanced_relationship_management
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.6.0
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubygems_version: 3.4.20
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Advanced Relationship Management for ActiveRecord Models
|
92
|
+
test_files: []
|