rails_age 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -15
- data/README.md +7 -4
- data/lib/apache_age/validators/{unique_edge_validator.rb → unique_edge.rb} +8 -4
- data/lib/apache_age/validators/{unique_vertex_validator.rb → unique_vertex.rb} +7 -3
- data/lib/generators/apache_age/edge/USAGE +18 -15
- data/lib/generators/apache_age/edge/edge_generator.rb +12 -41
- data/lib/generators/apache_age/edge/templates/edge.rb.tt +22 -14
- data/lib/generators/apache_age/generate_entity_methods.rb +84 -0
- data/lib/generators/apache_age/node/USAGE +7 -7
- data/lib/generators/apache_age/node/node_generator.rb +7 -82
- data/lib/generators/apache_age/node/templates/node.rb.tt +2 -2
- data/lib/rails_age/version.rb +1 -1
- data/lib/rails_age.rb +2 -2
- data/lib/tasks/add_age_migration.rake +69 -0
- data/lib/tasks/install.rake +2 -6
- data/lib/tasks/override_db_migrate.rake +50 -0
- metadata +7 -6
- data/lib/tasks/config_migrate.rake +0 -35
- data/lib/tasks/copy_migrations.rake +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ccc1964e751ea0670eeab5cc406f1d73d0ac4a188c3f0c97ff410cf0a12aa664
|
4
|
+
data.tar.gz: 9de0e59661db92464c6505c9dc3d6a2eb314f35333eeb4f97d92d2f4b628275b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15179f421eaa5fcf6180af0fd7865b1c69884064a2030b7f03300898eeda08d7bb2f871c43ec3b5ad1ae49db98b30cf274c582e3906fd66fe24b68812fb78b9a
|
7
|
+
data.tar.gz: 1a83d773effad75f2e90f89ee06f8585fa5a9fe40104df7913b0e0470d3026b7cc160ecb8e3ce3e2f2fcf7c1831a902fb61b1140974cc5f792d15e43ee3de4f1
|
data/CHANGELOG.md
CHANGED
@@ -1,42 +1,59 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## VERSION 0.
|
3
|
+
## VERSION 0.6.2 - 2024-xx-xx
|
4
4
|
|
5
|
-
- **
|
6
|
-
|
7
|
-
- **cypher**
|
5
|
+
- **cypher queries** (like active record queries)
|
8
6
|
* schema override
|
9
7
|
* query support
|
10
8
|
* paths support
|
11
9
|
* select attributes support
|
12
10
|
|
13
|
-
-
|
11
|
+
## VERSION 0.6.1 - 2024-xx-xx
|
12
|
+
|
13
|
+
- **Age Path**
|
14
|
+
|
15
|
+
## VERSION 0.6.0 - 2024-xx-xx
|
16
|
+
|
17
|
+
breaking change?: namespaces (by default) will use their own schema? (add to database.yml & schema.rb ?)
|
18
|
+
|
19
|
+
- **AGE Schema override**
|
20
|
+
|
21
|
+
- **multiple AGE Schema**
|
14
22
|
|
15
|
-
## VERSION 0.
|
23
|
+
## VERSION 0.5.3 - 2024-xx-xx
|
16
24
|
|
17
25
|
- **Edge Scaffold** (generates edge, type, view and controller)
|
18
26
|
* add `rails generate apache_age:edge_scaffold HasJob employee_role start_node:person end_node:company`
|
19
27
|
|
20
|
-
## VERSION 0.
|
28
|
+
## VERSION 0.5.2 - 2024-xx-xx
|
29
|
+
|
30
|
+
- **Edge Generator**
|
31
|
+
* add start-/end-nodes types to edge generator (would make scaffold easier), ie:
|
32
|
+
`rails generate apache_age:edge HasPet owner_role start_node:person end_node:pet`
|
33
|
+
with property and specified start-/end-nodes (person and pet nodes must have already been created)
|
34
|
+
|
35
|
+
## VERSION 0.5.1 - 2024-xx-xx
|
21
36
|
|
22
37
|
- **Node Scaffold** (generates node, type, view and controller)
|
23
38
|
* add `rails generate apache_age:node_scaffold Person first_name last_name age:integer`
|
24
39
|
|
25
|
-
## VERSION 0.
|
40
|
+
## VERSION 0.5.0 - 2024-06-15
|
41
|
+
|
42
|
+
**breaking change**: renamed the validators to UniqueVertex and UniqueEdge
|
26
43
|
|
27
44
|
- **Edge Generator**
|
28
|
-
* add `rails generate apache_age:edge HasPet owner_role`
|
29
|
-
|
30
|
-
with property and specified start-/end-nodes (person and pet nodes must have already been created)
|
45
|
+
* add `rails generate apache_age:edge HasPet owner_role`
|
46
|
+
caveate: start_node and end_node are of type `:vertex` in the generator but can be changed manually in the class file - having trouble with the generator loading the types (the generator rejects custom types - but rails still works with custom types)
|
31
47
|
|
32
|
-
## VERSION 0.4.1 - 2024-
|
48
|
+
## VERSION 0.4.1 - 2024-06-15
|
33
49
|
|
34
|
-
- **
|
35
|
-
* add
|
50
|
+
- **Installer**
|
51
|
+
* add optional: safe migrations with `bin/rails db:migrate` instead of using `bin/rails apache_age:migrate`
|
52
|
+
(install using `bin/rails apache_age:override_db_migrate`)
|
36
53
|
|
37
54
|
## VERSION 0.4.0 - 2024-06-14
|
38
55
|
|
39
|
-
|
56
|
+
**breaking change**: type (:vertix) is now required in core for edges
|
40
57
|
|
41
58
|
- **Installer**
|
42
59
|
* AGE types added to installer (with tests)
|
data/README.md
CHANGED
@@ -55,8 +55,11 @@ install Apache Age (you can ignore the `unknown OID` warnings)
|
|
55
55
|
bundle add rails_age
|
56
56
|
bundle install
|
57
57
|
bin/rails apache_age:install
|
58
|
+
# optional: prevents `bin/rails db:migrate` from modifying the schema file,
|
59
|
+
# alternatively you can use: `bin/rails apache_age:migrate` to run safe migrations
|
60
|
+
bin/rails apache_age:override_db_migrate
|
58
61
|
git add .
|
59
|
-
git commit -m "Add Apache Age
|
62
|
+
git commit -m "Add & configure Apache Age within Rails"
|
60
63
|
```
|
61
64
|
|
62
65
|
make some nodes :string is the default type
|
@@ -69,10 +72,10 @@ make some edges (`:vertex` is the default type) for start_node and end_node
|
|
69
72
|
```bash
|
70
73
|
# when start node and end node are not specified they are of type `:vertex`
|
71
74
|
# this is generally not recommended - exept when very generic relationships are needed
|
72
|
-
rails generate apache_age:edge HasJob employee_role
|
75
|
+
rails generate apache_age:edge HasJob employee_role begin_date:date
|
73
76
|
|
74
|
-
# this is recommended -
|
75
|
-
rails generate apache_age:node HasPet start_node:person end_node:pet caretaker_role
|
77
|
+
# # this is recommended - (but not yet working) add explicit start_node and end_node types manually
|
78
|
+
# rails generate apache_age:node HasPet start_node:person end_node:pet caretaker_role
|
76
79
|
```
|
77
80
|
|
78
81
|
**NOTE:** the default `rails db:migrate` inappropriately modifies the schema file. This installer patches the migration to prevent this (however this might break on rails updates, etc). **You can run `bin/rails apache_age:install` at any time to repair the schema file as needed.**
|
@@ -1,10 +1,14 @@
|
|
1
|
+
# lib/apache_age/validators/unique_edge.rb
|
2
|
+
|
1
3
|
# Usage (within an Age Model)
|
2
|
-
# validates_with
|
3
|
-
#
|
4
|
-
#
|
4
|
+
# validates_with(
|
5
|
+
# ApacheAge::Validators::UniqueEdge,
|
6
|
+
# attributes: [:start_id, :employee_role, :end_id]
|
7
|
+
# )
|
8
|
+
|
5
9
|
module ApacheAge
|
6
10
|
module Validators
|
7
|
-
class
|
11
|
+
class UniqueEdge < ActiveModel::Validator
|
8
12
|
def validate(record)
|
9
13
|
allowed_keys = record.age_properties.keys
|
10
14
|
attributes = options[:attributes] || []
|
@@ -1,10 +1,14 @@
|
|
1
|
+
# lib/apache_age/validators/unique_vertex.rb
|
2
|
+
|
1
3
|
# Usage (within an Age Model)
|
2
|
-
# validates_with
|
4
|
+
# validates_with(
|
5
|
+
# ApacheAge::Validators::UniqueVertex,
|
6
|
+
# attributes: [:first_name, :last_name, :gender]
|
7
|
+
# )
|
3
8
|
|
4
|
-
# lib/apache_age/validators/unique_vertex_validator.rb
|
5
9
|
module ApacheAge
|
6
10
|
module Validators
|
7
|
-
class
|
11
|
+
class UniqueVertex < ActiveModel::Validator
|
8
12
|
def validate(record)
|
9
13
|
allowed_keys = record.age_properties.keys
|
10
14
|
attributes = options[:attributes]
|
@@ -1,35 +1,38 @@
|
|
1
1
|
Description:
|
2
|
-
This creates Apache AGE
|
3
|
-
|
2
|
+
This creates Apache AGE edges that work seamlessly with Rails.
|
3
|
+
An edge can be created with or without a namespace.
|
4
4
|
See the below examples.
|
5
5
|
|
6
6
|
Example:
|
7
|
-
`bin/rails g apache_age:
|
7
|
+
`bin/rails g apache_age:edge HasJob employee_role start_date:date`
|
8
|
+
|
9
|
+
(hopefully comming soon - but not yet ready is the ability to add start_node and end_node types)
|
10
|
+
`bin/rails g apache_age:edge HasJob employee_role start_date:date start_node:person end_node:company`
|
8
11
|
|
9
12
|
This creates:
|
10
|
-
`app/
|
13
|
+
`app/edges/has_job.rb`
|
11
14
|
|
12
15
|
with the contents:
|
13
16
|
```
|
14
|
-
class
|
15
|
-
include ApacheAge::Entities::
|
17
|
+
class HasJob
|
18
|
+
include ApacheAge::Entities::Edge
|
16
19
|
|
17
|
-
attribute :
|
18
|
-
attribute :
|
20
|
+
attribute :employee_role, :string
|
21
|
+
attribute :start_date, :date
|
19
22
|
|
20
|
-
validates :
|
21
|
-
validates :
|
23
|
+
validates :employee_role, presence: true
|
24
|
+
validates :begin_date, presence: true
|
22
25
|
|
23
26
|
# unique node validator (remove any attributes that are not important to uniqueness)
|
24
27
|
validates_with(
|
25
|
-
ApacheAge::Validators::
|
26
|
-
attributes: [:
|
28
|
+
ApacheAge::Validators::UniqueVertex,
|
29
|
+
attributes: [:employee_role, :begin_date :start_node, :end_node]
|
27
30
|
)
|
28
31
|
end
|
29
32
|
```
|
30
33
|
|
31
34
|
A namespace can also be used:
|
32
|
-
`bin/rails g apache_age:
|
35
|
+
`bin/rails g apache_age:edge Animals/Cat name age:integer`
|
33
36
|
|
34
37
|
This creates:
|
35
38
|
`app/nodes/animals/cat.rb`
|
@@ -47,8 +50,8 @@ Example:
|
|
47
50
|
|
48
51
|
# unique node validator (remove any attributes that are not important to uniqueness)
|
49
52
|
validates_with(
|
50
|
-
ApacheAge::Validators::
|
51
|
-
attributes: [:name, :age]
|
53
|
+
ApacheAge::Validators::UniqueVertex,
|
54
|
+
attributes: [:name, :age, :start_node, :end_node]
|
52
55
|
)
|
53
56
|
end
|
54
57
|
```
|
@@ -1,56 +1,27 @@
|
|
1
|
+
# lib/generators/apache_age/edge/edge_generator.rb
|
1
2
|
require 'rails/generators'
|
2
3
|
require 'rails/generators/named_base'
|
3
4
|
|
5
|
+
require_relative '../generate_entity_methods'
|
6
|
+
# TODO: get generators to work with custom types!
|
7
|
+
# require_relative "#{Rails.root}/config/initializers/types"
|
8
|
+
|
4
9
|
module ApacheAge
|
5
10
|
class EdgeGenerator < Rails::Generators::NamedBase
|
11
|
+
include ApacheAge::GenerateEntityMethods
|
12
|
+
|
6
13
|
source_root File.expand_path('templates', __dir__)
|
7
14
|
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
8
15
|
|
9
16
|
def perform_task
|
10
|
-
|
17
|
+
age_type = 'edge'
|
18
|
+
Rails.application.eager_load! # Ensure all initializers and dependencies are loaded
|
19
|
+
behavior == :invoke ? generate_age_entity(age_type) : destroy_age_entity(age_type)
|
11
20
|
end
|
12
21
|
|
13
22
|
private
|
14
23
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
def destroy_edge_file
|
20
|
-
file_path = File.join("app/edge", class_path, "#{file_name}.rb")
|
21
|
-
File.delete(file_path) if File.exist?(file_path)
|
22
|
-
end
|
23
|
-
|
24
|
-
def attributes_list
|
25
|
-
attributes.map { |attr| { name: attr.name, type: attr.type } }
|
26
|
-
end
|
27
|
-
|
28
|
-
def unique_attributes
|
29
|
-
attributes_list.map { |attr| attr[:name].to_sym }
|
30
|
-
end
|
31
|
-
|
32
|
-
def parent_module
|
33
|
-
class_path.map(&:camelize).join('::')
|
34
|
-
end
|
35
|
-
|
36
|
-
def full_class_name
|
37
|
-
parent_module.empty? ? class_name : "#{parent_module}::#{class_name}"
|
38
|
-
end
|
39
|
-
|
40
|
-
def indented_namespace
|
41
|
-
return '' if parent_module.empty?
|
42
|
-
|
43
|
-
parent_module.split('::').map.with_index do |namespace, index|
|
44
|
-
"#{' ' * index}module #{namespace}"
|
45
|
-
end.join("\n") + "\n"
|
46
|
-
end
|
47
|
-
|
48
|
-
def indented_end_namespace
|
49
|
-
return '' if parent_module.empty?
|
50
|
-
|
51
|
-
parent_module.split('::').map.with_index do |_, index|
|
52
|
-
"#{' ' * (parent_module.split('::').length - 1 - index)}end"
|
53
|
-
end.join("\n") + "\n"
|
54
|
-
end
|
24
|
+
# different than node_generator.rb
|
25
|
+
def unique_edge_attributes = unique_attributes + [:start_node, :end_node]
|
55
26
|
end
|
56
27
|
end
|
@@ -1,20 +1,28 @@
|
|
1
|
-
<%=
|
2
|
-
|
3
|
-
<%= ' ' * class_path.length %> include ApacheAge::Entities::Edge
|
1
|
+
class <%= class_name %>
|
2
|
+
include ApacheAge::Entities::Edge
|
4
3
|
|
5
4
|
<%- attributes_list.each do |attribute| -%>
|
6
|
-
|
5
|
+
attribute :<%= attribute[:name] %>, :<%= attribute[:reference] || attribute[:type] %>
|
7
6
|
<%- end -%>
|
7
|
+
# recommendation for (start_node and end_node): change `:vertex` with the 'node' type
|
8
|
+
# see `config/initializers/apache_age.rb` for the list of available node types
|
9
|
+
attribute :start_node, :vertex
|
10
|
+
attribute :end_node, :vertex
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
<%- attributes_list.each do |attribute| -%>
|
13
|
+
validates :<%= attribute[:name] %>, presence: true
|
14
|
+
<%- end -%>
|
15
|
+
validates :start_node, presence: true
|
16
|
+
validates :end_node, presence: true
|
17
|
+
|
18
|
+
validate :validate_unique_edge
|
11
19
|
|
12
|
-
|
20
|
+
private
|
13
21
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
<%=
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
# custom unique edge validator (remove any attributes that are NOT important to uniqueness)
|
23
|
+
def validate_unique_edge
|
24
|
+
ApacheAge::Validators::UniqueEdge
|
25
|
+
.new(attributes: <%= unique_edge_attributes.inspect %>)
|
26
|
+
.validate(self)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# lib/generators/apache_age/generate_entity_methods.rb
|
2
|
+
|
3
|
+
module ApacheAge
|
4
|
+
module GenerateEntityMethods
|
5
|
+
def generate_age_entity(age_type)
|
6
|
+
template "#{age_type}.rb.tt", File.join(destination_root, "app/#{age_type}s", class_path, "#{file_name}.rb")
|
7
|
+
add_type_config
|
8
|
+
end
|
9
|
+
|
10
|
+
def destroy_age_entity(age_type)
|
11
|
+
file_path = File.join(destination_root, "app/#{age_type}s", class_path, "#{file_name}.rb")
|
12
|
+
File.delete(file_path) if File.exist?(file_path)
|
13
|
+
remove_type_config
|
14
|
+
end
|
15
|
+
|
16
|
+
def attributes_list
|
17
|
+
attributes.map { |attr| { name: attr.name, type: attr.type } }
|
18
|
+
end
|
19
|
+
|
20
|
+
def parent_module
|
21
|
+
class_path.map(&:camelize).join('::')
|
22
|
+
end
|
23
|
+
|
24
|
+
def unique_attributes
|
25
|
+
attributes_list.map { |attr| attr[:name].to_sym }
|
26
|
+
end
|
27
|
+
|
28
|
+
def full_class_name
|
29
|
+
parent_module.empty? ? class_name : "#{parent_module}::#{class_name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def indented_namespace
|
33
|
+
return '' if parent_module.empty?
|
34
|
+
|
35
|
+
parent_module.split('::').map.with_index do |namespace, index|
|
36
|
+
"#{' ' * index}module #{namespace}"
|
37
|
+
end.join("\n") + "\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
# def indented_end_namespace
|
41
|
+
# return '' if parent_module.empty?
|
42
|
+
|
43
|
+
# parent_module.split('::').map.with_index do |_, index|
|
44
|
+
# "#{' ' * (parent_module.split('::').length - 1 - index)}end"
|
45
|
+
# end.join("\n") + "\n"
|
46
|
+
# end
|
47
|
+
|
48
|
+
def add_type_config
|
49
|
+
return unless File.exist?(types_config_file)
|
50
|
+
|
51
|
+
types_content = File.read(types_config_file)
|
52
|
+
types_content.sub!(/^end\s*$/, "#{new_type_content}end")
|
53
|
+
File.open(types_config_file, 'w') { |file| file.write(types_content) }
|
54
|
+
puts " modified: config/initializers/types.rb"
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove_type_config
|
58
|
+
return unless File.exist?(types_config_file)
|
59
|
+
|
60
|
+
type_to_remove = new_type_content
|
61
|
+
|
62
|
+
types_content = File.read(types_config_file)
|
63
|
+
types_content.gsub!(type_to_remove, '')
|
64
|
+
File.open(types_config_file, 'w') { |file| file.write(types_content) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def types_config_file = File.join(Rails.root, 'config/initializers/types.rb')
|
68
|
+
|
69
|
+
def new_type_content
|
70
|
+
file_path = [class_path, file_name].reject(&:blank?).join('/').downcase
|
71
|
+
entity_namespace = class_path.map(&:capitalize).join('::')
|
72
|
+
entity_class_name = file_name.split('_').map(&:capitalize).join
|
73
|
+
entity_namespaced_class = [entity_namespace, entity_class_name].reject(&:blank?).join('::')
|
74
|
+
type_name = [class_path.join('_'), file_name].reject(&:blank?).join('_')
|
75
|
+
content =
|
76
|
+
<<-RUBY
|
77
|
+
require_dependency '#{file_path}'
|
78
|
+
ActiveModel::Type.register(
|
79
|
+
:#{type_name}, ApacheAge::Types::AgeTypeGenerator.create_type_for(#{entity_namespaced_class})
|
80
|
+
)
|
81
|
+
RUBY
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -14,16 +14,16 @@ Example:
|
|
14
14
|
class Cat
|
15
15
|
include ApacheAge::Entities::Vertex
|
16
16
|
|
17
|
-
attribute :
|
18
|
-
attribute :
|
17
|
+
attribute :full_name, :string
|
18
|
+
attribute :birthdate, :date
|
19
19
|
|
20
|
-
validates :
|
21
|
-
validates :
|
20
|
+
validates :full_name, presence: true
|
21
|
+
validates :birthdate, presence: true
|
22
22
|
|
23
23
|
# unique node validator (remove any attributes that are not important to uniqueness)
|
24
24
|
validates_with(
|
25
|
-
ApacheAge::Validators::
|
26
|
-
attributes: [:
|
25
|
+
ApacheAge::Validators::UniqueVertex,
|
26
|
+
attributes: [:full_name, :birthdate]
|
27
27
|
)
|
28
28
|
end
|
29
29
|
```
|
@@ -47,7 +47,7 @@ Example:
|
|
47
47
|
|
48
48
|
# unique node validator (remove any attributes that are not important to uniqueness)
|
49
49
|
validates_with(
|
50
|
-
ApacheAge::Validators::
|
50
|
+
ApacheAge::Validators::UniqueVertex,
|
51
51
|
attributes: [:name, :age]
|
52
52
|
)
|
53
53
|
end
|
@@ -1,94 +1,19 @@
|
|
1
|
+
# lib/generators/apache_age/node/entity_generator.rb
|
1
2
|
require 'rails/generators'
|
2
3
|
require 'rails/generators/named_base'
|
3
4
|
|
5
|
+
require_relative '../generate_entity_methods'
|
6
|
+
|
4
7
|
module ApacheAge
|
5
8
|
class NodeGenerator < Rails::Generators::NamedBase
|
9
|
+
include ApacheAge::GenerateEntityMethods
|
10
|
+
|
6
11
|
source_root File.expand_path('templates', __dir__)
|
7
12
|
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
8
13
|
|
9
14
|
def perform_task
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def create_node_file
|
16
|
-
template "node.rb.tt", File.join(destination_root, "app/nodes", class_path, "#{file_name}.rb")
|
17
|
-
add_type_config
|
18
|
-
end
|
19
|
-
|
20
|
-
def destroy_node_file
|
21
|
-
file_path = File.join(destination_root, "app/nodes", class_path, "#{file_name}.rb")
|
22
|
-
File.delete(file_path) if File.exist?(file_path)
|
23
|
-
remove_type_config
|
24
|
-
end
|
25
|
-
|
26
|
-
def attributes_list
|
27
|
-
attributes.map { |attr| { name: attr.name, type: attr.type } }
|
28
|
-
end
|
29
|
-
|
30
|
-
def unique_attributes
|
31
|
-
attributes_list.map { |attr| attr[:name].to_sym }
|
32
|
-
end
|
33
|
-
|
34
|
-
def parent_module
|
35
|
-
class_path.map(&:camelize).join('::')
|
36
|
-
end
|
37
|
-
|
38
|
-
def full_class_name
|
39
|
-
parent_module.empty? ? class_name : "#{parent_module}::#{class_name}"
|
40
|
-
end
|
41
|
-
|
42
|
-
def indented_namespace
|
43
|
-
return '' if parent_module.empty?
|
44
|
-
|
45
|
-
parent_module.split('::').map.with_index do |namespace, index|
|
46
|
-
"#{' ' * index}module #{namespace}"
|
47
|
-
end.join("\n") + "\n"
|
48
|
-
end
|
49
|
-
|
50
|
-
def indented_end_namespace
|
51
|
-
return '' if parent_module.empty?
|
52
|
-
|
53
|
-
parent_module.split('::').map.with_index do |_, index|
|
54
|
-
"#{' ' * (parent_module.split('::').length - 1 - index)}end"
|
55
|
-
end.join("\n") + "\n"
|
56
|
-
end
|
57
|
-
|
58
|
-
def add_type_config
|
59
|
-
return unless File.exist?(types_config_file)
|
60
|
-
|
61
|
-
types_content = File.read(types_config_file)
|
62
|
-
types_content.sub!(/^end\s*$/, "#{new_type_content}end")
|
63
|
-
File.open(types_config_file, 'w') { |file| file.write(types_content) }
|
64
|
-
puts " modified: config/initializers/types.rb"
|
65
|
-
end
|
66
|
-
|
67
|
-
def remove_type_config
|
68
|
-
return unless File.exist?(types_config_file)
|
69
|
-
|
70
|
-
type_to_remove = new_type_content
|
71
|
-
|
72
|
-
types_content = File.read(types_config_file)
|
73
|
-
types_content.gsub!(type_to_remove, '')
|
74
|
-
File.open(types_config_file, 'w') { |file| file.write(types_content) }
|
75
|
-
end
|
76
|
-
|
77
|
-
def types_config_file = File.join(Rails.root, 'config/initializers/types.rb')
|
78
|
-
|
79
|
-
def new_type_content
|
80
|
-
file_path = [class_path, file_name].reject(&:blank?).join('/').downcase
|
81
|
-
node_namespace = class_path.map(&:capitalize).join('::')
|
82
|
-
node_class_name = file_name.split('_').map(&:capitalize).join
|
83
|
-
node_namespaced_class = [node_namespace, node_class_name].reject(&:blank?).join('::')
|
84
|
-
type_name = [class_path.join('_'), file_name].reject(&:blank?).join('_')
|
85
|
-
content =
|
86
|
-
<<-RUBY
|
87
|
-
require_dependency '#{file_path}'
|
88
|
-
ActiveModel::Type.register(
|
89
|
-
:#{type_name}, ApacheAge::Types::AgeTypeGenerator.create_type_for(#{node_namespaced_class})
|
90
|
-
)
|
91
|
-
RUBY
|
15
|
+
age_type = 'node'
|
16
|
+
behavior == :invoke ? generate_age_entity(age_type) : destroy_age_entity(age_type)
|
92
17
|
end
|
93
18
|
end
|
94
19
|
end
|
@@ -9,9 +9,9 @@ class <%= class_name %>
|
|
9
9
|
validates :<%= attribute[:name] %>, presence: true
|
10
10
|
<%- end -%>
|
11
11
|
|
12
|
-
# unique node validator (remove any attributes that are
|
12
|
+
# custom unique node validator (remove any attributes that are NOT important to uniqueness)
|
13
13
|
validates_with(
|
14
|
-
ApacheAge::Validators::
|
14
|
+
ApacheAge::Validators::UniqueVertex,
|
15
15
|
attributes: <%= unique_attributes.inspect %>
|
16
16
|
)
|
17
17
|
end
|
data/lib/rails_age/version.rb
CHANGED
data/lib/rails_age.rb
CHANGED
@@ -11,7 +11,7 @@ module ApacheAge
|
|
11
11
|
require 'apache_age/entities/edge'
|
12
12
|
require 'apache_age/entities/entity'
|
13
13
|
require 'apache_age/entities/vertex'
|
14
|
+
require 'apache_age/validators/unique_edge'
|
15
|
+
require 'apache_age/validators/unique_vertex'
|
14
16
|
require 'apache_age/types/age_type_generator'
|
15
|
-
require 'apache_age/validators/unique_edge_validator'
|
16
|
-
require 'apache_age/validators/unique_vertex_validator'
|
17
17
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# lib/tasks/install.rake
|
2
|
+
# Usage:
|
3
|
+
# * `bin/rails apache_age:add_age_migration`
|
4
|
+
# * `bin/rails apache_age:add_age_migration[destination_path.to_s]`
|
5
|
+
# * `bundle exec rails apache_age:add_age_migration.invoke(destination_path.to_s)`
|
6
|
+
namespace :apache_age do
|
7
|
+
desc "Copy migrations from rails_age to application and update schema"
|
8
|
+
task :add_age_migration, [:destination_path] => :environment do |t, args|
|
9
|
+
|
10
|
+
base_name = 'add_apache_age'
|
11
|
+
destination_path =
|
12
|
+
File.expand_path(args[:destination_path].presence || "#{Rails.root}/db/migrate", __FILE__)
|
13
|
+
|
14
|
+
FileUtils.mkdir_p(destination_path) unless File.exist?(destination_path)
|
15
|
+
existing_migrations =
|
16
|
+
Dir.glob("#{destination_path}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
|
17
|
+
|
18
|
+
if existing_migrations.any? { |migration| migration.include?(base_name) }
|
19
|
+
puts "Skipping migration AddApacheAge, it already exists"
|
20
|
+
else
|
21
|
+
age_migration_contents =
|
22
|
+
<<~RUBY
|
23
|
+
class AddApacheAge < ActiveRecord::Migration[7.1]
|
24
|
+
def up
|
25
|
+
# Allow age extension
|
26
|
+
execute('CREATE EXTENSION IF NOT EXISTS age;')
|
27
|
+
|
28
|
+
# Load the age code
|
29
|
+
execute("LOAD 'age';")
|
30
|
+
|
31
|
+
# Load the ag_catalog into the search path
|
32
|
+
execute('SET search_path = ag_catalog, "$user", public;')
|
33
|
+
|
34
|
+
# Create age_schema graph if it doesn't exist
|
35
|
+
execute("SELECT create_graph('age_schema');")
|
36
|
+
end
|
37
|
+
|
38
|
+
def down
|
39
|
+
execute <<-SQL
|
40
|
+
DO $$
|
41
|
+
BEGIN
|
42
|
+
IF EXISTS (
|
43
|
+
SELECT 1
|
44
|
+
FROM pg_constraint
|
45
|
+
WHERE conname = 'fk_graph_oid'
|
46
|
+
) THEN
|
47
|
+
ALTER TABLE ag_catalog.ag_label
|
48
|
+
DROP CONSTRAINT fk_graph_oid;
|
49
|
+
END IF;
|
50
|
+
END $$;
|
51
|
+
SQL
|
52
|
+
|
53
|
+
execute("SELECT drop_graph('age_schema', true);")
|
54
|
+
execute('DROP SCHEMA IF EXISTS ag_catalog CASCADE;')
|
55
|
+
execute('DROP EXTENSION IF EXISTS age;')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
RUBY
|
59
|
+
|
60
|
+
migration_version = Time.now.utc.strftime("%Y%m%d%H%M%S")
|
61
|
+
file_version = migration_version.delete('_')
|
62
|
+
new_filename = "#{file_version}_#{base_name}.rb"
|
63
|
+
destination_file = File.join(destination_path, new_filename)
|
64
|
+
|
65
|
+
File.write(destination_file, age_migration_contents)
|
66
|
+
puts "Created migration AddApacheAge"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/tasks/install.rake
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
namespace :apache_age do
|
5
5
|
desc "Install & configure Apache Age within Rails (updates migrations, schema & database.yml)"
|
6
6
|
task :install => :environment do
|
7
|
-
#
|
8
|
-
Rake::Task["apache_age:
|
7
|
+
# Ensure the AGE migration is in place
|
8
|
+
Rake::Task["apache_age:add_age_migration"].invoke
|
9
9
|
|
10
10
|
# run any new migrations
|
11
11
|
Rake::Task["db:migrate"].invoke
|
@@ -18,9 +18,5 @@ namespace :apache_age do
|
|
18
18
|
|
19
19
|
# ensure the config/initializers/types.rb file has the base AGE Types
|
20
20
|
Rake::Task["apache_age:config_types"].invoke
|
21
|
-
|
22
|
-
# # ensure bin/rails db:migrate is always followed with
|
23
|
-
# # `Rake::Task["apache_age:config_schema"].invoke` to ensure the schema isn't mangled
|
24
|
-
# Rake::Task["apache_age:config_migrate"].invoke
|
25
21
|
end
|
26
22
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
namespace :apache_age do
|
2
|
+
desc "Ensure 'db:migrate' is followed by 'apache_age:config_schema' to repair the schema.rb file after migration mangles it."
|
3
|
+
task :override_db_migrate, [:destination_path] => :environment do |t, args|
|
4
|
+
destination_path = (args[:destination_path].presence || "#{Rails.root}") + "/bin"
|
5
|
+
FileUtils.mkdir_p(destination_path) unless File.exist?(destination_path)
|
6
|
+
bin_rails_path = File.expand_path("#{destination_path}/rails", __FILE__)
|
7
|
+
|
8
|
+
original_content = File.read(bin_rails_path)
|
9
|
+
destination_content = original_content.dup
|
10
|
+
|
11
|
+
unless destination_content.include?("#!/usr/bin/env ruby\nrails_cmd = ARGV.first")
|
12
|
+
capture_rails_cmd =
|
13
|
+
<<~RUBY
|
14
|
+
rails_cmd = ARGV.first # must be first (otherwise consumed by rails:commands)
|
15
|
+
RUBY
|
16
|
+
|
17
|
+
# add to the top of the file (with gsub)
|
18
|
+
destination_content.sub!(
|
19
|
+
%r{#!/usr/bin/env ruby\n},
|
20
|
+
"#!/usr/bin/env ruby\n#{capture_rails_cmd}\n"
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Check if the migration hook is already present
|
25
|
+
unless destination_content.include?('Rake::Task["apache_age:config_schema"].invoke')
|
26
|
+
override_migrate =
|
27
|
+
<<~RUBY
|
28
|
+
|
29
|
+
# ensure db:migrate is followed with: `Rake::Task["apache_age:config_schema"].invoke`
|
30
|
+
# to the schema.rb file after the migration mangles it
|
31
|
+
if rails_cmd == 'db:migrate'
|
32
|
+
require 'rake'
|
33
|
+
Rails.application.load_tasks
|
34
|
+
Rake::Task['db:migrate'].invoke
|
35
|
+
Rake::Task["apache_age:config_schema"].invoke
|
36
|
+
end
|
37
|
+
RUBY
|
38
|
+
|
39
|
+
# append to the end of the file
|
40
|
+
destination_content << override_migrate
|
41
|
+
end
|
42
|
+
|
43
|
+
if destination_content == original_content
|
44
|
+
puts "AGE Safe Migration is already present in bin/rails. (If its not working inspect the bin/rails file)"
|
45
|
+
else
|
46
|
+
File.write(bin_rails_path, destination_content)
|
47
|
+
puts "AGE Safe Migration added to bin/rails. Now run `bin/rails db:migrate`, then run your tests (or inspect the schema.rb file)."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_age
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bill Tihen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -73,25 +73,26 @@ files:
|
|
73
73
|
- lib/apache_age/entities/entity.rb
|
74
74
|
- lib/apache_age/entities/vertex.rb
|
75
75
|
- lib/apache_age/types/age_type_generator.rb
|
76
|
-
- lib/apache_age/validators/
|
77
|
-
- lib/apache_age/validators/
|
76
|
+
- lib/apache_age/validators/unique_edge.rb
|
77
|
+
- lib/apache_age/validators/unique_vertex.rb
|
78
78
|
- lib/apache_age/validators/vertex_type_validator.rb
|
79
79
|
- lib/generators/apache_age/edge/USAGE
|
80
80
|
- lib/generators/apache_age/edge/edge_generator.rb
|
81
81
|
- lib/generators/apache_age/edge/templates/edge.rb.tt
|
82
|
+
- lib/generators/apache_age/generate_entity_methods.rb
|
82
83
|
- lib/generators/apache_age/node/USAGE
|
83
84
|
- lib/generators/apache_age/node/node_generator.rb
|
84
85
|
- lib/generators/apache_age/node/templates/node.rb.tt
|
85
86
|
- lib/rails_age.rb
|
86
87
|
- lib/rails_age/engine.rb
|
87
88
|
- lib/rails_age/version.rb
|
89
|
+
- lib/tasks/add_age_migration.rake
|
88
90
|
- lib/tasks/config_database.rake
|
89
|
-
- lib/tasks/config_migrate.rake
|
90
91
|
- lib/tasks/config_schema.rake
|
91
92
|
- lib/tasks/config_types.rake
|
92
|
-
- lib/tasks/copy_migrations.rake
|
93
93
|
- lib/tasks/install.rake
|
94
94
|
- lib/tasks/migrate.rake
|
95
|
+
- lib/tasks/override_db_migrate.rake
|
95
96
|
homepage: https://github.com/marpori/rails_age
|
96
97
|
licenses:
|
97
98
|
- MIT
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# namespace :apache_age do
|
2
|
-
# desc "Ensure db:migrate is followed by apache_age:config_schema"
|
3
|
-
# task :config_migrate do
|
4
|
-
# bin_rails_path = File.expand_path('../../../bin/rails', __FILE__)
|
5
|
-
# migration_hook =
|
6
|
-
# <<-RUBY
|
7
|
-
# # ensure db:migrate is followed with: `Rake::Task["apache_age:config_schema"].invoke`
|
8
|
-
# # which repairs the schema.rb file after the migration mangles it
|
9
|
-
# require_relative '../config/boot'
|
10
|
-
# require 'rails/commands'
|
11
|
-
|
12
|
-
# if ARGV.first == 'db:migrate'
|
13
|
-
# require 'rake'
|
14
|
-
# Rails.application.load_tasks
|
15
|
-
# Rake::Task['db:migrate'].invoke
|
16
|
-
# Rake::Task["apache_age:config_schema"].invoke
|
17
|
-
# else
|
18
|
-
# Rake::Task['rails:commands'].invoke(*ARGV)
|
19
|
-
# end
|
20
|
-
# RUBY
|
21
|
-
|
22
|
-
# # Read the current content of the bin/rails file
|
23
|
-
# bin_rails_content = File.read(bin_rails_path)
|
24
|
-
|
25
|
-
# # Check if the migration hook is already present
|
26
|
-
# unless bin_rails_content.include?('Rake::Task["apache_age:config_schema"].invoke')
|
27
|
-
# # Append the migration hook to the end of the bin/rails file
|
28
|
-
# File.open(bin_rails_path, 'a') do |file|
|
29
|
-
# file.write(migration_hook)
|
30
|
-
# end
|
31
|
-
# puts "Migration hook added to bin/rails."
|
32
|
-
# else
|
33
|
-
# puts "Migration hook already present in bin/rails."
|
34
|
-
# end
|
35
|
-
# end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# lib/tasks/install.rake
|
2
|
-
# Usage:
|
3
|
-
# * `bin/rails apache_age:copy_migrations`
|
4
|
-
# * `bundle exec rails apache_age:copy_migrations[destination_path.to_s]`
|
5
|
-
# * `bundle exec rails apache_age:copy_migrations.invoke(destination_path.to_s)`
|
6
|
-
namespace :apache_age do
|
7
|
-
desc "Copy migrations from rails_age to application and update schema"
|
8
|
-
task :copy_migrations, [:destination_path] => :environment do |t, args|
|
9
|
-
source = File.expand_path('../../../db/migrate', __FILE__)
|
10
|
-
destination_path =
|
11
|
-
File.expand_path(args[:destination_path].presence || "#{Rails.root}/db/migrate", __FILE__)
|
12
|
-
|
13
|
-
FileUtils.mkdir_p(destination_path) unless File.exist?(destination_path)
|
14
|
-
existing_migrations =
|
15
|
-
Dir.glob("#{destination_path}/*.rb").map { |file| File.basename(file).sub(/^\d+/, '') }
|
16
|
-
|
17
|
-
Dir.glob("#{source}/*.rb").each do |file|
|
18
|
-
filename = File.basename(file)
|
19
|
-
test_name = filename.sub(/^\d+/, '')
|
20
|
-
|
21
|
-
if existing_migrations.include?(test_name)
|
22
|
-
puts "Skipping migration: '#{filename}', it already exists"
|
23
|
-
else
|
24
|
-
migration_version = Time.now.utc.strftime("%Y_%m_%d_%H%M%S")
|
25
|
-
file_version = migration_version.delete('_')
|
26
|
-
new_filename = filename.sub(/^\d+/, file_version)
|
27
|
-
destination_file = File.join(destination_path, new_filename)
|
28
|
-
FileUtils.cp(file, destination_file)
|
29
|
-
puts "Created migration: '#{new_filename}'"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|